blob: 533998b033b74d287943f959d9d30b787f13fb08 [file] [log] [blame]
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -07001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_MULTI_HTTP_FETCHER_H__
6#define CHROMEOS_PLATFORM_UPDATE_ENGINE_MULTI_HTTP_FETCHER_H__
7
8#include <tr1/memory>
9#include <utility>
10#include <vector>
11
12#include "update_engine/http_fetcher.h"
Darin Petkov9ce452b2010-11-17 14:33:28 -080013#include "update_engine/utils.h"
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070014
15// This class is a simple wrapper around an HttpFetcher. The client
16// specifies a vector of byte ranges. MultiHttpFetcher will fetch bytes
17// from those offsets. Pass -1 as a length to specify unlimited length.
18// It really only would make sense for the last range specified to have
19// unlimited length.
20
21namespace chromeos_update_engine {
22
23template<typename BaseHttpFetcher>
24class MultiHttpFetcher : public HttpFetcher, public HttpFetcherDelegate {
25 public:
26 typedef std::vector<std::pair<off_t, off_t> > RangesVect;
27
28 MultiHttpFetcher()
29 : sent_transfer_complete_(false),
Darin Petkov9ce452b2010-11-17 14:33:28 -080030 pending_next_fetcher_(false),
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070031 current_index_(0),
32 bytes_received_this_fetcher_(0) {}
33 ~MultiHttpFetcher() {}
34
35 void set_ranges(const RangesVect& ranges) {
36 ranges_ = ranges;
37 fetchers_.resize(ranges_.size()); // Allocate the fetchers
38 for (typename std::vector<std::tr1::shared_ptr<BaseHttpFetcher>
39 >::iterator it = fetchers_.begin(), e = fetchers_.end();
40 it != e; ++it) {
41 (*it) = std::tr1::shared_ptr<BaseHttpFetcher>(new BaseHttpFetcher);
42 (*it)->set_delegate(this);
43 }
44 }
Darin Petkov9b230572010-10-08 10:20:09 -070045
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070046 void SetOffset(off_t offset) {} // for now, doesn't support this
47
48 // Begins the transfer to the specified URL.
49 void BeginTransfer(const std::string& url) {
50 url_ = url;
51 if (ranges_.empty()) {
52 if (delegate_)
53 delegate_->TransferComplete(this, true);
54 return;
55 }
56 current_index_ = 0;
57 LOG(INFO) << "starting first transfer";
58 StartTransfer();
59 }
60
Darin Petkov9ce452b2010-11-17 14:33:28 -080061 void TransferTerminated(HttpFetcher* fetcher) {
62 LOG(INFO) << "Received transfer terminated.";
63 if (pending_next_fetcher_) {
64 pending_next_fetcher_ = false;
65 if (++current_index_ >= ranges_.size()) {
66 SendTransferComplete(fetcher, true);
67 } else {
68 StartTransfer();
69 }
70 return;
71 }
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070072 current_index_ = ranges_.size();
73 sent_transfer_complete_ = true; // a fib
Darin Petkov9ce452b2010-11-17 14:33:28 -080074 if (delegate_) {
75 // Note that after the callback returns this object may be destroyed.
76 delegate_->TransferTerminated(this);
77 }
78 }
79
80 void TerminateTransfer() {
81 // If the current fetcher is already being terminated, just wait for its
82 // TransferTerminated callback.
83 if (pending_next_fetcher_) {
84 pending_next_fetcher_ = false;
85 return;
86 }
87 // If there's a current active fetcher terminate it and wait for its
88 // TransferTerminated callback.
89 if (current_index_ < fetchers_.size()) {
90 fetchers_[current_index_]->TerminateTransfer();
91 return;
92 }
93 // Transfer is terminated before it got started and before any ranges were
94 // added.
95 TransferTerminated(this);
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070096 }
97
98 void Pause() {
99 if (current_index_ < fetchers_.size())
100 fetchers_[current_index_]->Pause();
101 }
102
103 void Unpause() {
104 if (current_index_ < fetchers_.size())
105 fetchers_[current_index_]->Unpause();
106 }
107
Darin Petkovfc7a0ce2010-10-25 10:38:37 -0700108 // These functions are overloaded in LibcurlHttp fetcher for testing purposes.
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700109 void set_idle_seconds(int seconds) {
110 for (typename std::vector<std::tr1::shared_ptr<BaseHttpFetcher> >::iterator
111 it = fetchers_.begin(),
112 e = fetchers_.end(); it != e; ++it) {
113 (*it)->set_idle_seconds(seconds);
114 }
115 }
116 void set_retry_seconds(int seconds) {
117 for (typename std::vector<std::tr1::shared_ptr<BaseHttpFetcher> >::iterator
118 it = fetchers_.begin(),
119 e = fetchers_.end(); it != e; ++it) {
120 (*it)->set_retry_seconds(seconds);
121 }
122 }
Darin Petkovfc7a0ce2010-10-25 10:38:37 -0700123 void SetConnectionAsExpensive(bool is_expensive) {
124 for (typename std::vector<std::tr1::shared_ptr<BaseHttpFetcher> >::iterator
125 it = fetchers_.begin(),
126 e = fetchers_.end(); it != e; ++it) {
127 (*it)->SetConnectionAsExpensive(is_expensive);
128 }
129 }
130 void SetBuildType(bool is_official) {
131 for (typename std::vector<std::tr1::shared_ptr<BaseHttpFetcher> >::iterator
132 it = fetchers_.begin(),
133 e = fetchers_.end(); it != e; ++it) {
134 (*it)->SetBuildType(is_official);
135 }
136 }
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700137
138 private:
139 void SendTransferComplete(HttpFetcher* fetcher, bool successful) {
140 if (sent_transfer_complete_)
141 return;
142 LOG(INFO) << "Sending transfer complete";
143 sent_transfer_complete_ = true;
144 http_response_code_ = fetcher->http_response_code();
145 if (delegate_)
146 delegate_->TransferComplete(this, successful);
147 }
Darin Petkov9b230572010-10-08 10:20:09 -0700148
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700149 void StartTransfer() {
150 if (current_index_ >= ranges_.size()) {
151 return;
152 }
Darin Petkov9b230572010-10-08 10:20:09 -0700153 LOG(INFO) << "Starting a transfer @" << ranges_[current_index_].first << "("
154 << ranges_[current_index_].second << ")";
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700155 bytes_received_this_fetcher_ = 0;
156 fetchers_[current_index_]->SetOffset(ranges_[current_index_].first);
Andrew de los Reyes34e41a12010-10-26 20:07:58 -0700157 if (delegate_)
158 delegate_->SeekToOffset(ranges_[current_index_].first);
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700159 fetchers_[current_index_]->BeginTransfer(url_);
160 }
Darin Petkov9b230572010-10-08 10:20:09 -0700161
Darin Petkov9ce452b2010-11-17 14:33:28 -0800162 void ReceivedBytes(HttpFetcher* fetcher, const char* bytes, int length) {
163 TEST_AND_RETURN(current_index_ < ranges_.size());
164 TEST_AND_RETURN(fetcher == fetchers_[current_index_].get());
165 TEST_AND_RETURN(!pending_next_fetcher_);
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700166 off_t next_size = length;
167 if (ranges_[current_index_].second >= 0) {
168 next_size = std::min(next_size,
169 ranges_[current_index_].second -
170 bytes_received_this_fetcher_);
171 }
172 LOG_IF(WARNING, next_size <= 0) << "Asked to write length <= 0";
Andrew de los Reyes34e41a12010-10-26 20:07:58 -0700173 if (delegate_) {
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700174 delegate_->ReceivedBytes(this, bytes, next_size);
Andrew de los Reyes34e41a12010-10-26 20:07:58 -0700175 }
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700176 bytes_received_this_fetcher_ += length;
177 if (ranges_[current_index_].second >= 0 &&
178 bytes_received_this_fetcher_ >= ranges_[current_index_].second) {
Darin Petkov9ce452b2010-11-17 14:33:28 -0800179 // Terminates the current fetcher. Waits for its TransferTerminated
180 // callback before starting the next fetcher so that we don't end up
181 // signalling the delegate that the whole multi-transfer is complete
182 // before all fetchers are really done and cleaned up.
183 pending_next_fetcher_ = true;
184 fetcher->TerminateTransfer();
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700185 }
186 }
187
188 void TransferComplete(HttpFetcher* fetcher, bool successful) {
Darin Petkov9ce452b2010-11-17 14:33:28 -0800189 TEST_AND_RETURN(!pending_next_fetcher_);
190 LOG(INFO) << "Received transfer complete.";
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700191 if (current_index_ >= ranges_.size()) {
192 SendTransferComplete(fetcher, true);
193 return;
194 }
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700195 if (ranges_[current_index_].second < 0) {
196 // We're done with the current operation
197 current_index_++;
198 if (current_index_ >= ranges_.size() || !successful) {
199 SendTransferComplete(fetcher, successful);
200 } else {
201 // Do the next transfer
202 StartTransfer();
203 }
204 return;
205 }
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700206 if (bytes_received_this_fetcher_ < ranges_[current_index_].second) {
207 LOG(WARNING) << "Received insufficient bytes from fetcher. "
208 << "Ending early";
209 SendTransferComplete(fetcher, false);
210 return;
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700211 }
Darin Petkov9ce452b2010-11-17 14:33:28 -0800212 LOG(INFO) << "Got spurious TransferComplete. Ignoring.";
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700213 }
Darin Petkov9b230572010-10-08 10:20:09 -0700214
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700215 // If true, do not send any more data or TransferComplete to the delegate.
Darin Petkovfc7a0ce2010-10-25 10:38:37 -0700216 bool sent_transfer_complete_;
Darin Petkov9b230572010-10-08 10:20:09 -0700217
Darin Petkov9ce452b2010-11-17 14:33:28 -0800218 // If true, the next fetcher needs to be started when TransferTerminated is
219 // received from the current fetcher.
220 bool pending_next_fetcher_;
221
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700222 RangesVect ranges_;
223 std::vector<std::tr1::shared_ptr<BaseHttpFetcher> > fetchers_;
Darin Petkov9b230572010-10-08 10:20:09 -0700224
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700225 RangesVect::size_type current_index_; // index into ranges_, fetchers_
226 off_t bytes_received_this_fetcher_;
227
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700228 DISALLOW_COPY_AND_ASSIGN(MultiHttpFetcher);
229};
230
231} // namespace chromeos_update_engine
232
233#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_MULTI_HTTP_FETCHER_H__