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#ifndef UPDATE_ENGINE_COMMON_MULTI_RANGE_HTTP_FETCHER_H_
18#define UPDATE_ENGINE_COMMON_MULTI_RANGE_HTTP_FETCHER_H_
19
20#include <deque>
21#include <memory>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "update_engine/common/http_fetcher.h"
27
28// This class is a simple wrapper around an HttpFetcher. The client
29// specifies a vector of byte ranges. MultiRangeHttpFetcher will fetch bytes
30// from those offsets, using the same bash fetcher for all ranges. Thus, the
31// fetcher must support beginning a transfer after one has stopped. Pass -1
32// as a length to specify unlimited length. It really only would make sense
33// for the last range specified to have unlimited length, tho it is legal for
34// other entries to have unlimited length.
35
36// There are three states a MultiRangeHttpFetcher object will be in:
37// - Stopped (start state)
38// - Downloading
39// - Pending transfer ended
40// Various functions below that might change state indicate possible
41// state changes.
42
43namespace chromeos_update_engine {
44
45class MultiRangeHttpFetcher : public HttpFetcher, public HttpFetcherDelegate {
46 public:
47  // Takes ownership of the passed in fetcher.
48  explicit MultiRangeHttpFetcher(HttpFetcher* base_fetcher)
49      : HttpFetcher(base_fetcher->proxy_resolver()),
50        base_fetcher_(base_fetcher),
51        base_fetcher_active_(false),
52        pending_transfer_ended_(false),
53        terminating_(false),
54        current_index_(0),
55        bytes_received_this_range_(0) {}
56  ~MultiRangeHttpFetcher() override {}
57
58  void ClearRanges() { ranges_.clear(); }
59
60  void AddRange(off_t offset, size_t size) {
61    CHECK_GT(size, static_cast<size_t>(0));
62    ranges_.push_back(Range(offset, size));
63  }
64
65  void AddRange(off_t offset) {
66    ranges_.push_back(Range(offset));
67  }
68
69  // HttpFetcher overrides.
70  void SetOffset(off_t offset) override {}  // for now, doesn't support this
71
72  void SetLength(size_t length) override {}  // unsupported
73  void UnsetLength() override {}
74
75  // Begins the transfer to the specified URL.
76  // State change: Stopped -> Downloading
77  // (corner case: Stopped -> Stopped for an empty request)
78  void BeginTransfer(const std::string& url) override;
79
80  // State change: Downloading -> Pending transfer ended
81  void TerminateTransfer() override;
82
83  void SetHeader(const std::string& header_name,
84                 const std::string& header_value) override {
85    base_fetcher_->SetHeader(header_name, header_value);
86  }
87
88  void Pause() override { base_fetcher_->Pause(); }
89
90  void Unpause() override { base_fetcher_->Unpause(); }
91
92  // These functions are overloaded in LibcurlHttp fetcher for testing purposes.
93  void set_idle_seconds(int seconds) override {
94    base_fetcher_->set_idle_seconds(seconds);
95  }
96  void set_retry_seconds(int seconds) override {
97    base_fetcher_->set_retry_seconds(seconds);
98  }
99  // TODO(deymo): Determine if this method should be virtual in HttpFetcher so
100  // this call is sent to the base_fetcher_.
101  virtual void SetProxies(const std::deque<std::string>& proxies) {
102    base_fetcher_->SetProxies(proxies);
103  }
104
105  inline size_t GetBytesDownloaded() override {
106    return base_fetcher_->GetBytesDownloaded();
107  }
108
109  void set_low_speed_limit(int low_speed_bps, int low_speed_sec) override {
110    base_fetcher_->set_low_speed_limit(low_speed_bps, low_speed_sec);
111  }
112
113  void set_connect_timeout(int connect_timeout_seconds) override {
114    base_fetcher_->set_connect_timeout(connect_timeout_seconds);
115  }
116
117  void set_max_retry_count(int max_retry_count) override {
118    base_fetcher_->set_max_retry_count(max_retry_count);
119  }
120
121 private:
122  // A range object defining the offset and length of a download chunk.  Zero
123  // length indicates an unspecified end offset (note that it is impossible to
124  // request a zero-length range in HTTP).
125  class Range {
126   public:
127    Range(off_t offset, size_t length) : offset_(offset), length_(length) {}
128    explicit Range(off_t offset) : offset_(offset), length_(0) {}
129
130    inline off_t offset() const { return offset_; }
131    inline size_t length() const { return length_; }
132
133    inline bool HasLength() const { return (length_ > 0); }
134
135    std::string ToString() const;
136
137   private:
138    off_t offset_;
139    size_t length_;
140  };
141
142  typedef std::vector<Range> RangesVect;
143
144  // State change: Stopped or Downloading -> Downloading
145  void StartTransfer();
146
147  // HttpFetcherDelegate overrides.
148  // State change: Downloading -> Downloading or Pending transfer ended
149  void ReceivedBytes(HttpFetcher* fetcher,
150                     const void* bytes,
151                     size_t length) override;
152
153  // State change: Pending transfer ended -> Stopped
154  void TransferEnded(HttpFetcher* fetcher, bool successful);
155  // These two call TransferEnded():
156  void TransferComplete(HttpFetcher* fetcher, bool successful) override;
157  void TransferTerminated(HttpFetcher* fetcher) override;
158
159  void Reset();
160
161  std::unique_ptr<HttpFetcher> base_fetcher_;
162
163  // If true, do not send any more data or TransferComplete to the delegate.
164  bool base_fetcher_active_;
165
166  // If true, the next fetcher needs to be started when TransferTerminated is
167  // received from the current fetcher.
168  bool pending_transfer_ended_;
169
170  // True if we are waiting for base fetcher to terminate b/c we are
171  // ourselves terminating.
172  bool terminating_;
173
174  RangesVect ranges_;
175
176  RangesVect::size_type current_index_;  // index into ranges_
177  size_t bytes_received_this_range_;
178
179  DISALLOW_COPY_AND_ASSIGN(MultiRangeHttpFetcher);
180};
181
182}  // namespace chromeos_update_engine
183
184#endif  // UPDATE_ENGINE_COMMON_MULTI_RANGE_HTTP_FETCHER_H_
185