blob: 63b85c9fab94d1ef63a4ad398bb6921587c3b385 [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
Alex Deymoaab50e32014-11-10 19:55:35 -08005#include "update_engine/multi_range_http_fetcher.h"
6
Alex Vakulenko75039d72014-03-25 12:36:28 -07007#include <base/strings/stringprintf.h>
Andrew de los Reyes819fef22010-12-17 11:33:58 -08008
Alex Vakulenkod2779df2014-06-16 13:19:00 -07009#include <algorithm>
10#include <string>
11
Andrew de los Reyes819fef22010-12-17 11:33:58 -080012#include "update_engine/utils.h"
13
14namespace chromeos_update_engine {
15
16// Begins the transfer to the specified URL.
17// State change: Stopped -> Downloading
18// (corner case: Stopped -> Stopped for an empty request)
Gilad Arnold9bedeb52011-11-17 16:19:57 -080019void MultiRangeHttpFetcher::BeginTransfer(const std::string& url) {
Andrew de los Reyes819fef22010-12-17 11:33:58 -080020 CHECK(!base_fetcher_active_) << "BeginTransfer but already active.";
21 CHECK(!pending_transfer_ended_) << "BeginTransfer but pending.";
22 CHECK(!terminating_) << "BeginTransfer but terminating.";
23
24 if (ranges_.empty()) {
25 // Note that after the callback returns this object may be destroyed.
26 if (delegate_)
27 delegate_->TransferComplete(this, true);
28 return;
29 }
30 url_ = url;
31 current_index_ = 0;
32 bytes_received_this_range_ = 0;
33 LOG(INFO) << "starting first transfer";
34 base_fetcher_->set_delegate(this);
35 StartTransfer();
36}
37
38// State change: Downloading -> Pending transfer ended
Gilad Arnold9bedeb52011-11-17 16:19:57 -080039void MultiRangeHttpFetcher::TerminateTransfer() {
Andrew de los Reyes819fef22010-12-17 11:33:58 -080040 if (!base_fetcher_active_) {
41 LOG(INFO) << "Called TerminateTransfer but not active.";
42 // Note that after the callback returns this object may be destroyed.
43 if (delegate_)
44 delegate_->TransferTerminated(this);
45 return;
46 }
47 terminating_ = true;
Alex Vakulenkod2779df2014-06-16 13:19:00 -070048
Andrew de los Reyes819fef22010-12-17 11:33:58 -080049 if (!pending_transfer_ended_) {
50 base_fetcher_->TerminateTransfer();
51 }
52}
53
54// State change: Stopped or Downloading -> Downloading
Gilad Arnold9bedeb52011-11-17 16:19:57 -080055void MultiRangeHttpFetcher::StartTransfer() {
Andrew de los Reyes819fef22010-12-17 11:33:58 -080056 if (current_index_ >= ranges_.size()) {
57 return;
58 }
Gilad Arnolde4ad2502011-12-29 17:08:54 -080059
60 Range range = ranges_[current_index_];
61 LOG(INFO) << "starting transfer of range " << range.ToString();
62
Andrew de los Reyes819fef22010-12-17 11:33:58 -080063 bytes_received_this_range_ = 0;
Gilad Arnolde4ad2502011-12-29 17:08:54 -080064 base_fetcher_->SetOffset(range.offset());
65 if (range.HasLength())
66 base_fetcher_->SetLength(range.length());
67 else
68 base_fetcher_->UnsetLength();
Andrew de los Reyes819fef22010-12-17 11:33:58 -080069 if (delegate_)
Gilad Arnolde4ad2502011-12-29 17:08:54 -080070 delegate_->SeekToOffset(range.offset());
Andrew de los Reyes819fef22010-12-17 11:33:58 -080071 base_fetcher_active_ = true;
72 base_fetcher_->BeginTransfer(url_);
73}
74
75// State change: Downloading -> Downloading or Pending transfer ended
Gilad Arnold9bedeb52011-11-17 16:19:57 -080076void MultiRangeHttpFetcher::ReceivedBytes(HttpFetcher* fetcher,
Andrew de los Reyes819fef22010-12-17 11:33:58 -080077 const char* bytes,
78 int length) {
79 CHECK_LT(current_index_, ranges_.size());
80 CHECK_EQ(fetcher, base_fetcher_.get());
81 CHECK(!pending_transfer_ended_);
Gilad Arnolde4ad2502011-12-29 17:08:54 -080082 size_t next_size = length;
83 Range range = ranges_[current_index_];
84 if (range.HasLength()) {
Andrew de los Reyes819fef22010-12-17 11:33:58 -080085 next_size = std::min(next_size,
Gilad Arnolde4ad2502011-12-29 17:08:54 -080086 range.length() - bytes_received_this_range_);
Andrew de los Reyes819fef22010-12-17 11:33:58 -080087 }
88 LOG_IF(WARNING, next_size <= 0) << "Asked to write length <= 0";
89 if (delegate_) {
90 delegate_->ReceivedBytes(this, bytes, next_size);
91 }
92 bytes_received_this_range_ += length;
Gilad Arnolde4ad2502011-12-29 17:08:54 -080093 if (range.HasLength() && bytes_received_this_range_ >= range.length()) {
Andrew de los Reyes819fef22010-12-17 11:33:58 -080094 // Terminates the current fetcher. Waits for its TransferTerminated
95 // callback before starting the next range so that we don't end up
96 // signalling the delegate that the whole multi-transfer is complete
97 // before all fetchers are really done and cleaned up.
98 pending_transfer_ended_ = true;
99 LOG(INFO) << "terminating transfer";
100 fetcher->TerminateTransfer();
101 }
102}
103
104// State change: Downloading or Pending transfer ended -> Stopped
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800105void MultiRangeHttpFetcher::TransferEnded(HttpFetcher* fetcher,
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800106 bool successful) {
107 CHECK(base_fetcher_active_) << "Transfer ended unexpectedly.";
108 CHECK_EQ(fetcher, base_fetcher_.get());
109 pending_transfer_ended_ = false;
110 http_response_code_ = fetcher->http_response_code();
111 LOG(INFO) << "TransferEnded w/ code " << http_response_code_;
112 if (terminating_) {
113 LOG(INFO) << "Terminating.";
114 Reset();
115 // Note that after the callback returns this object may be destroyed.
116 if (delegate_)
117 delegate_->TransferTerminated(this);
118 return;
119 }
120
121 // If we didn't get enough bytes, it's failure
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800122 Range range = ranges_[current_index_];
123 if (range.HasLength()) {
124 if (bytes_received_this_range_ < range.length()) {
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800125 // Failure
126 LOG(INFO) << "Didn't get enough bytes. Ending w/ failure.";
127 Reset();
128 // Note that after the callback returns this object may be destroyed.
129 if (delegate_)
130 delegate_->TransferComplete(this, false);
131 return;
132 }
133 // We got enough bytes and there were bytes specified, so this is success.
134 successful = true;
135 }
136
Andrew de los Reyesffcb6d12011-04-04 09:57:38 -0700137 // If we have another transfer, do that.
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800138 if (current_index_ + 1 < ranges_.size()) {
139 current_index_++;
140 LOG(INFO) << "Starting next transfer (" << current_index_ << ").";
141 StartTransfer();
142 return;
143 }
144
145 LOG(INFO) << "Done w/ all transfers";
146 Reset();
147 // Note that after the callback returns this object may be destroyed.
148 if (delegate_)
149 delegate_->TransferComplete(this, successful);
150}
151
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800152void MultiRangeHttpFetcher::TransferComplete(HttpFetcher* fetcher,
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800153 bool successful) {
154 LOG(INFO) << "Received transfer complete.";
155 TransferEnded(fetcher, successful);
156}
157
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800158void MultiRangeHttpFetcher::TransferTerminated(HttpFetcher* fetcher) {
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800159 LOG(INFO) << "Received transfer terminated.";
160 TransferEnded(fetcher, false);
161}
162
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800163void MultiRangeHttpFetcher::Reset() {
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800164 base_fetcher_active_ = pending_transfer_ended_ = terminating_ = false;
165 current_index_ = 0;
166 bytes_received_this_range_ = 0;
167}
168
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800169std::string MultiRangeHttpFetcher::Range::ToString() const {
Alex Vakulenko75039d72014-03-25 12:36:28 -0700170 std::string range_str = base::StringPrintf("%jd+", offset());
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800171 if (HasLength())
172 base::StringAppendF(&range_str, "%zu", length());
173 else
174 base::StringAppendF(&range_str, "?");
175 return range_str;
176}
177
Andrew de los Reyes819fef22010-12-17 11:33:58 -0800178} // namespace chromeos_update_engine