blob: f19b9c39d666d8eda40a0822bf6e81319871dcdb [file] [log] [blame]
Andrew de los Reyes819fef22010-12-17 11:33:58 -08001// 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
Gilad Arnolde4ad2502011-12-29 17:08:54 -08005#include <base/stringprintf.h>
Andrew de los Reyes819fef22010-12-17 11:33:58 -08006
Gilad Arnolde4ad2502011-12-29 17:08:54 -08007#include "update_engine/multi_range_http_fetcher.h"
Andrew de los Reyes819fef22010-12-17 11:33:58 -08008#include "update_engine/utils.h"
9
10namespace chromeos_update_engine {
11
12// Begins the transfer to the specified URL.
13// State change: Stopped -> Downloading
14// (corner case: Stopped -> Stopped for an empty request)
Gilad Arnold9bedeb52011-11-17 16:19:57 -080015void MultiRangeHttpFetcher::BeginTransfer(const std::string& url) {
Andrew de los Reyes819fef22010-12-17 11:33:58 -080016 CHECK(!base_fetcher_active_) << "BeginTransfer but already active.";
17 CHECK(!pending_transfer_ended_) << "BeginTransfer but pending.";
18 CHECK(!terminating_) << "BeginTransfer but terminating.";
19
20 if (ranges_.empty()) {
21 // Note that after the callback returns this object may be destroyed.
22 if (delegate_)
23 delegate_->TransferComplete(this, true);
24 return;
25 }
26 url_ = url;
27 current_index_ = 0;
28 bytes_received_this_range_ = 0;
29 LOG(INFO) << "starting first transfer";
30 base_fetcher_->set_delegate(this);
31 StartTransfer();
32}
33
34// State change: Downloading -> Pending transfer ended
Gilad Arnold9bedeb52011-11-17 16:19:57 -080035void MultiRangeHttpFetcher::TerminateTransfer() {
Andrew de los Reyes819fef22010-12-17 11:33:58 -080036 if (!base_fetcher_active_) {
37 LOG(INFO) << "Called TerminateTransfer but not active.";
38 // Note that after the callback returns this object may be destroyed.
39 if (delegate_)
40 delegate_->TransferTerminated(this);
41 return;
42 }
43 terminating_ = true;
44
45 if (!pending_transfer_ended_) {
46 base_fetcher_->TerminateTransfer();
47 }
48}
49
50// State change: Stopped or Downloading -> Downloading
Gilad Arnold9bedeb52011-11-17 16:19:57 -080051void MultiRangeHttpFetcher::StartTransfer() {
Andrew de los Reyes819fef22010-12-17 11:33:58 -080052 if (current_index_ >= ranges_.size()) {
53 return;
54 }
Gilad Arnolde4ad2502011-12-29 17:08:54 -080055
56 Range range = ranges_[current_index_];
57 LOG(INFO) << "starting transfer of range " << range.ToString();
58
Andrew de los Reyes819fef22010-12-17 11:33:58 -080059 bytes_received_this_range_ = 0;
Gilad Arnolde4ad2502011-12-29 17:08:54 -080060 base_fetcher_->SetOffset(range.offset());
61 if (range.HasLength())
62 base_fetcher_->SetLength(range.length());
63 else
64 base_fetcher_->UnsetLength();
Andrew de los Reyes819fef22010-12-17 11:33:58 -080065 if (delegate_)
Gilad Arnolde4ad2502011-12-29 17:08:54 -080066 delegate_->SeekToOffset(range.offset());
Andrew de los Reyes819fef22010-12-17 11:33:58 -080067 base_fetcher_active_ = true;
68 base_fetcher_->BeginTransfer(url_);
69}
70
71// State change: Downloading -> Downloading or Pending transfer ended
Gilad Arnold9bedeb52011-11-17 16:19:57 -080072void MultiRangeHttpFetcher::ReceivedBytes(HttpFetcher* fetcher,
Andrew de los Reyes819fef22010-12-17 11:33:58 -080073 const char* bytes,
74 int length) {
75 CHECK_LT(current_index_, ranges_.size());
76 CHECK_EQ(fetcher, base_fetcher_.get());
77 CHECK(!pending_transfer_ended_);
Gilad Arnolde4ad2502011-12-29 17:08:54 -080078 size_t next_size = length;
79 Range range = ranges_[current_index_];
80 if (range.HasLength()) {
Andrew de los Reyes819fef22010-12-17 11:33:58 -080081 next_size = std::min(next_size,
Gilad Arnolde4ad2502011-12-29 17:08:54 -080082 range.length() - bytes_received_this_range_);
Andrew de los Reyes819fef22010-12-17 11:33:58 -080083 }
84 LOG_IF(WARNING, next_size <= 0) << "Asked to write length <= 0";
85 if (delegate_) {
86 delegate_->ReceivedBytes(this, bytes, next_size);
87 }
88 bytes_received_this_range_ += length;
Gilad Arnolde4ad2502011-12-29 17:08:54 -080089 if (range.HasLength() && bytes_received_this_range_ >= range.length()) {
Andrew de los Reyes819fef22010-12-17 11:33:58 -080090 // Terminates the current fetcher. Waits for its TransferTerminated
91 // callback before starting the next range so that we don't end up
92 // signalling the delegate that the whole multi-transfer is complete
93 // before all fetchers are really done and cleaned up.
94 pending_transfer_ended_ = true;
95 LOG(INFO) << "terminating transfer";
96 fetcher->TerminateTransfer();
97 }
98}
99
100// State change: Downloading or Pending transfer ended -> Stopped
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800101void MultiRangeHttpFetcher::TransferEnded(HttpFetcher* fetcher,
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800102 bool successful) {
103 CHECK(base_fetcher_active_) << "Transfer ended unexpectedly.";
104 CHECK_EQ(fetcher, base_fetcher_.get());
105 pending_transfer_ended_ = false;
106 http_response_code_ = fetcher->http_response_code();
107 LOG(INFO) << "TransferEnded w/ code " << http_response_code_;
108 if (terminating_) {
109 LOG(INFO) << "Terminating.";
110 Reset();
111 // Note that after the callback returns this object may be destroyed.
112 if (delegate_)
113 delegate_->TransferTerminated(this);
114 return;
115 }
116
117 // If we didn't get enough bytes, it's failure
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800118 Range range = ranges_[current_index_];
119 if (range.HasLength()) {
120 if (bytes_received_this_range_ < range.length()) {
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800121 // Failure
122 LOG(INFO) << "Didn't get enough bytes. Ending w/ failure.";
123 Reset();
124 // Note that after the callback returns this object may be destroyed.
125 if (delegate_)
126 delegate_->TransferComplete(this, false);
127 return;
128 }
129 // We got enough bytes and there were bytes specified, so this is success.
130 successful = true;
131 }
132
Andrew de los Reyesffcb6d12011-04-04 09:57:38 -0700133 // If we have another transfer, do that.
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800134 if (current_index_ + 1 < ranges_.size()) {
135 current_index_++;
136 LOG(INFO) << "Starting next transfer (" << current_index_ << ").";
137 StartTransfer();
138 return;
139 }
140
141 LOG(INFO) << "Done w/ all transfers";
142 Reset();
143 // Note that after the callback returns this object may be destroyed.
144 if (delegate_)
145 delegate_->TransferComplete(this, successful);
146}
147
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800148void MultiRangeHttpFetcher::TransferComplete(HttpFetcher* fetcher,
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800149 bool successful) {
150 LOG(INFO) << "Received transfer complete.";
151 TransferEnded(fetcher, successful);
152}
153
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800154void MultiRangeHttpFetcher::TransferTerminated(HttpFetcher* fetcher) {
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800155 LOG(INFO) << "Received transfer terminated.";
156 TransferEnded(fetcher, false);
157}
158
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800159void MultiRangeHttpFetcher::Reset() {
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800160 base_fetcher_active_ = pending_transfer_ended_ = terminating_ = false;
161 current_index_ = 0;
162 bytes_received_this_range_ = 0;
163}
164
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800165std::string MultiRangeHttpFetcher::Range::ToString() const {
166 std::string range_str = StringPrintf("%jd+", offset());
167 if (HasLength())
168 base::StringAppendF(&range_str, "%zu", length());
169 else
170 base::StringAppendF(&range_str, "?");
171 return range_str;
172}
173
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800174} // namespace chromeos_update_engine