| // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef UPDATE_ENGINE_MULTI_RANGE_HTTP_FETCHER_H_ |
| #define UPDATE_ENGINE_MULTI_RANGE_HTTP_FETCHER_H_ |
| |
| #include <deque> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "update_engine/http_fetcher.h" |
| |
| // This class is a simple wrapper around an HttpFetcher. The client |
| // specifies a vector of byte ranges. MultiRangeHttpFetcher will fetch bytes |
| // from those offsets, using the same bash fetcher for all ranges. Thus, the |
| // fetcher must support beginning a transfer after one has stopped. Pass -1 |
| // as a length to specify unlimited length. It really only would make sense |
| // for the last range specified to have unlimited length, tho it is legal for |
| // other entries to have unlimited length. |
| |
| // There are three states a MultiRangeHttpFetcher object will be in: |
| // - Stopped (start state) |
| // - Downloading |
| // - Pending transfer ended |
| // Various functions below that might change state indicate possible |
| // state changes. |
| |
| namespace chromeos_update_engine { |
| |
| class MultiRangeHttpFetcher : public HttpFetcher, public HttpFetcherDelegate { |
| public: |
| // Takes ownership of the passed in fetcher. |
| explicit MultiRangeHttpFetcher(HttpFetcher* base_fetcher) |
| : HttpFetcher(base_fetcher->proxy_resolver(), |
| base_fetcher->GetSystemState()), |
| base_fetcher_(base_fetcher), |
| base_fetcher_active_(false), |
| pending_transfer_ended_(false), |
| terminating_(false), |
| current_index_(0), |
| bytes_received_this_range_(0) {} |
| ~MultiRangeHttpFetcher() override {} |
| |
| void ClearRanges() { ranges_.clear(); } |
| |
| void AddRange(off_t offset, size_t size) { |
| CHECK_GT(size, static_cast<size_t>(0)); |
| ranges_.push_back(Range(offset, size)); |
| } |
| |
| void AddRange(off_t offset) { |
| ranges_.push_back(Range(offset)); |
| } |
| |
| // HttpFetcher overrides. |
| void SetOffset(off_t offset) override {} // for now, doesn't support this |
| |
| void SetLength(size_t length) override {} // unsupported |
| void UnsetLength() override {} |
| |
| // Begins the transfer to the specified URL. |
| // State change: Stopped -> Downloading |
| // (corner case: Stopped -> Stopped for an empty request) |
| void BeginTransfer(const std::string& url) override; |
| |
| // State change: Downloading -> Pending transfer ended |
| void TerminateTransfer() override; |
| |
| void Pause() override { base_fetcher_->Pause(); } |
| |
| void Unpause() override { base_fetcher_->Unpause(); } |
| |
| // These functions are overloaded in LibcurlHttp fetcher for testing purposes. |
| void set_idle_seconds(int seconds) override { |
| base_fetcher_->set_idle_seconds(seconds); |
| } |
| void set_retry_seconds(int seconds) override { |
| base_fetcher_->set_retry_seconds(seconds); |
| } |
| // TODO(deymo): Determine if this method should be virtual in HttpFetcher so |
| // this call is sent to the base_fetcher_. |
| virtual void SetProxies(const std::deque<std::string>& proxies) { |
| base_fetcher_->SetProxies(proxies); |
| } |
| |
| inline size_t GetBytesDownloaded() override { |
| return base_fetcher_->GetBytesDownloaded(); |
| } |
| |
| void set_low_speed_limit(int low_speed_bps, int low_speed_sec) override { |
| base_fetcher_->set_low_speed_limit(low_speed_bps, low_speed_sec); |
| } |
| |
| void set_connect_timeout(int connect_timeout_seconds) override { |
| base_fetcher_->set_connect_timeout(connect_timeout_seconds); |
| } |
| |
| void set_max_retry_count(int max_retry_count) override { |
| base_fetcher_->set_max_retry_count(max_retry_count); |
| } |
| |
| private: |
| // A range object defining the offset and length of a download chunk. Zero |
| // length indicates an unspecified end offset (note that it is impossible to |
| // request a zero-length range in HTTP). |
| class Range { |
| public: |
| Range(off_t offset, size_t length) : offset_(offset), length_(length) {} |
| explicit Range(off_t offset) : offset_(offset), length_(0) {} |
| |
| inline off_t offset() const { return offset_; } |
| inline size_t length() const { return length_; } |
| |
| inline bool HasLength() const { return (length_ > 0); } |
| |
| std::string ToString() const; |
| |
| private: |
| off_t offset_; |
| size_t length_; |
| }; |
| |
| typedef std::vector<Range> RangesVect; |
| |
| // State change: Stopped or Downloading -> Downloading |
| void StartTransfer(); |
| |
| // HttpFetcherDelegate overrides. |
| // State change: Downloading -> Downloading or Pending transfer ended |
| void ReceivedBytes(HttpFetcher* fetcher, |
| const void* bytes, |
| size_t length) override; |
| |
| // State change: Pending transfer ended -> Stopped |
| void TransferEnded(HttpFetcher* fetcher, bool successful); |
| // These two call TransferEnded(): |
| void TransferComplete(HttpFetcher* fetcher, bool successful) override; |
| void TransferTerminated(HttpFetcher* fetcher) override; |
| |
| void Reset(); |
| |
| std::unique_ptr<HttpFetcher> base_fetcher_; |
| |
| // If true, do not send any more data or TransferComplete to the delegate. |
| bool base_fetcher_active_; |
| |
| // If true, the next fetcher needs to be started when TransferTerminated is |
| // received from the current fetcher. |
| bool pending_transfer_ended_; |
| |
| // True if we are waiting for base fetcher to terminate b/c we are |
| // ourselves terminating. |
| bool terminating_; |
| |
| RangesVect ranges_; |
| |
| RangesVect::size_type current_index_; // index into ranges_ |
| size_t bytes_received_this_range_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MultiRangeHttpFetcher); |
| }; |
| |
| } // namespace chromeos_update_engine |
| |
| #endif // UPDATE_ENGINE_MULTI_RANGE_HTTP_FETCHER_H_ |