AU multi-range fetcher requests properly closed ranges when their length
is known.
* HttpFetcher allows to set the length of data to be fetched.
LibcurlHttpFetcher uses this value in applying the appropriate libcurl
option (CURLOPT_RANGE). MultiHttpFetcher sets the desired payload
length in the underlying fetcher accordingly.
* Improved functionality of test_http_server: (a) correctly parses
closed range intervals; (b) generalized response header generation;
(c) unified and generalized get handling for both stable and flaky
cases.
* Small scale refactoring, improved logging and readability.
BUG=chromium-os:24666
TEST=unit tests
Change-Id: I1727710ca747088c67a68305f355da683b07b6a3
Reviewed-on: https://gerrit.chromium.org/gerrit/13594
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
Commit-Ready: Gilad Arnold <garnold@chromium.org>
diff --git a/http_common.h b/http_common.h
index ecd90e1..807ee7d 100644
--- a/http_common.h
+++ b/http_common.h
@@ -32,6 +32,7 @@
kHttpResponseForbidden = 403,
kHttpResponseNotFound = 404,
kHttpResponseRequestTimeout = 408,
+ kHttpResponseReqRangeNotSat = 416,
kHttpResponseInternalServerError = 500,
kHttpResponseNotImplemented = 501,
kHttpResponseServiceUnavailable = 503,
diff --git a/http_fetcher.h b/http_fetcher.h
index a14e854..46f6903 100644
--- a/http_fetcher.h
+++ b/http_fetcher.h
@@ -67,6 +67,10 @@
// Downloading should resume from this offset
virtual void SetOffset(off_t offset) = 0;
+ // Set/unset the length of the range to be downloaded.
+ virtual void SetLength(size_t length) = 0;
+ virtual void UnsetLength() = 0;
+
// Begins the transfer to the specified URL. This fetcher instance should not
// be destroyed until either TransferComplete, or TransferTerminated is
// called.
diff --git a/http_fetcher_unittest.cc b/http_fetcher_unittest.cc
index 1ffba72..7a931ac 100644
--- a/http_fetcher_unittest.cc
+++ b/http_fetcher_unittest.cc
@@ -253,7 +253,7 @@
MultiRangeHttpFetcher *ret =
new MultiRangeHttpFetcher(new LibcurlHttpFetcher(resolver));
ret->ClearRanges();
- ret->AddRange(0, -1);
+ ret->AddRange(0);
// Speed up test execution.
ret->set_idle_seconds(1);
ret->set_retry_seconds(1);
@@ -806,11 +806,13 @@
public:
MultiHttpFetcherTestDelegate(int expected_response_code)
: expected_response_code_(expected_response_code) {}
+
virtual void ReceivedBytes(HttpFetcher* fetcher,
const char* bytes, int length) {
EXPECT_EQ(fetcher, fetcher_.get());
data.append(bytes, length);
}
+
virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
EXPECT_EQ(fetcher, fetcher_.get());
EXPECT_EQ(expected_response_code_ != kHttpResponseUndefined, successful);
@@ -820,9 +822,11 @@
fetcher_.reset(NULL);
g_main_loop_quit(loop_);
}
+
virtual void TransferTerminated(HttpFetcher* fetcher) {
ADD_FAILURE();
}
+
scoped_ptr<HttpFetcher> fetcher_;
int expected_response_code_;
string data;
@@ -846,8 +850,15 @@
multi_fetcher->ClearRanges();
for (vector<pair<off_t, off_t> >::const_iterator it = ranges.begin(),
e = ranges.end(); it != e; ++it) {
- LOG(INFO) << "Adding range";
- multi_fetcher->AddRange(it->first, it->second);
+ std::string tmp_str = StringPrintf("%jd+", it->first);
+ if (it->second > 0) {
+ base::StringAppendF(&tmp_str, "%jd", it->second);
+ multi_fetcher->AddRange(it->first, it->second);
+ } else {
+ base::StringAppendF(&tmp_str, "?");
+ multi_fetcher->AddRange(it->first);
+ }
+ LOG(INFO) << "added range: " << tmp_str;
}
multi_fetcher->SetConnectionAsExpensive(false);
multi_fetcher->SetBuildType(false);
@@ -875,7 +886,7 @@
vector<pair<off_t, off_t> > ranges;
ranges.push_back(make_pair(0, 25));
- ranges.push_back(make_pair(99, -1));
+ ranges.push_back(make_pair(99, 0));
MultiTest(this->test_.NewLargeFetcher(),
this->test_.BigUrl(),
ranges,
@@ -898,7 +909,7 @@
ranges,
"abcdefghijabcdefghijabcd",
24,
- kHttpResponseOk);
+ kHttpResponsePartialContent);
}
TYPED_TEST(HttpFetcherTest, MultiHttpFetcherMultiEndTest) {
@@ -909,8 +920,8 @@
ASSERT_TRUE(server->started_);
vector<pair<off_t, off_t> > ranges;
- ranges.push_back(make_pair(kBigLength - 2, -1));
- ranges.push_back(make_pair(kBigLength - 3, -1));
+ ranges.push_back(make_pair(kBigLength - 2, 0));
+ ranges.push_back(make_pair(kBigLength - 3, 0));
MultiTest(this->test_.NewLargeFetcher(),
this->test_.BigUrl(),
ranges,
@@ -954,7 +965,7 @@
vector<pair<off_t, off_t> > ranges;
ranges.push_back(make_pair(0, 25));
- ranges.push_back(make_pair(99, -1));
+ ranges.push_back(make_pair(99, 0));
MultiTest(this->test_.NewLargeFetcher(3),
LocalServerUrlForPath(base::StringPrintf("/error-if-offset/%d/2",
kBigLength)),
@@ -975,7 +986,7 @@
vector<pair<off_t, off_t> > ranges;
ranges.push_back(make_pair(0, 25));
- ranges.push_back(make_pair(99, -1));
+ ranges.push_back(make_pair(99, 0));
MultiTest(this->test_.NewLargeFetcher(2),
LocalServerUrlForPath(base::StringPrintf("/error-if-offset/%d/3",
kBigLength)),
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index 05d03fa..df376d5 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -8,6 +8,7 @@
#include <string>
#include <base/logging.h>
+#include <base/stringprintf.h>
#include "update_engine/certificate_checker.h"
#include "update_engine/chrome_proxy_resolver.h"
@@ -92,12 +93,26 @@
CURLE_OK);
}
- if (bytes_downloaded_ > 0) {
- // Resume from where we left off
+ if (bytes_downloaded_ > 0 || download_length_) {
+ // Resume from where we left off.
resume_offset_ = bytes_downloaded_;
- CHECK_EQ(curl_easy_setopt(curl_handle_,
- CURLOPT_RESUME_FROM_LARGE,
- bytes_downloaded_), CURLE_OK);
+ CHECK_GE(resume_offset_, 0);
+
+ // Compute end offset, if one is specified. As per HTTP specification, this
+ // is an inclusive boundary. Make sure it doesn't overflow.
+ size_t end_offset = 0;
+ if (download_length_) {
+ end_offset = static_cast<size_t>(resume_offset_) + download_length_ - 1;
+ CHECK_LE((size_t) resume_offset_, end_offset);
+ }
+
+ // Create a string representation of the desired range.
+ std::string range_str = (end_offset ?
+ StringPrintf("%jd-%zu", resume_offset_,
+ end_offset) :
+ StringPrintf("%jd-", resume_offset_));
+ CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_RANGE, range_str.c_str()),
+ CURLE_OK);
}
CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this), CURLE_OK);
diff --git a/libcurl_http_fetcher.h b/libcurl_http_fetcher.h
index d9d1697..ff8589b 100644
--- a/libcurl_http_fetcher.h
+++ b/libcurl_http_fetcher.h
@@ -33,6 +33,7 @@
transfer_in_progress_(false),
transfer_size_(0),
bytes_downloaded_(0),
+ download_length_(0),
resume_offset_(0),
retry_count_(0),
retry_seconds_(60),
@@ -51,7 +52,10 @@
// Cleans up all internal state. Does not notify delegate
~LibcurlHttpFetcher();
- void SetOffset(off_t offset) { bytes_downloaded_ = offset; }
+ virtual void SetOffset(off_t offset) { bytes_downloaded_ = offset; }
+
+ virtual void SetLength(size_t length) { download_length_ = length; }
+ virtual void UnsetLength() { SetLength(0); }
// Begins the transfer if it hasn't already begun.
virtual void BeginTransfer(const std::string& url);
@@ -207,6 +211,10 @@
// How many bytes have been downloaded and sent to the delegate.
off_t bytes_downloaded_;
+ // The remaining maximum number of bytes to download. Zero represents an
+ // unspecified length.
+ size_t download_length_;
+
// If we resumed an earlier transfer, data offset that we used for the
// new connection. 0 otherwise.
// In this class, resume refers to resuming a dropped HTTP connection,
diff --git a/mock_http_fetcher.h b/mock_http_fetcher.h
index 0fd9537..31171f1 100644
--- a/mock_http_fetcher.h
+++ b/mock_http_fetcher.h
@@ -51,6 +51,10 @@
delegate_->SeekToOffset(offset);
}
+ // Do nothing.
+ virtual void SetLength(size_t length) {}
+ virtual void UnsetLength() {}
+
// Dummy: no bytes were downloaded.
virtual size_t GetBytesDownloaded() {
return sent_size_;
diff --git a/multi_range_http_fetcher.cc b/multi_range_http_fetcher.cc
index 40ef1e8..f19b9c3 100644
--- a/multi_range_http_fetcher.cc
+++ b/multi_range_http_fetcher.cc
@@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "update_engine/multi_range_http_fetcher.h"
+#include <base/stringprintf.h>
+#include "update_engine/multi_range_http_fetcher.h"
#include "update_engine/utils.h"
namespace chromeos_update_engine {
@@ -51,12 +52,18 @@
if (current_index_ >= ranges_.size()) {
return;
}
- LOG(INFO) << "Starting a transfer @" << ranges_[current_index_].first << "("
- << ranges_[current_index_].second << ")";
+
+ Range range = ranges_[current_index_];
+ LOG(INFO) << "starting transfer of range " << range.ToString();
+
bytes_received_this_range_ = 0;
- base_fetcher_->SetOffset(ranges_[current_index_].first);
+ base_fetcher_->SetOffset(range.offset());
+ if (range.HasLength())
+ base_fetcher_->SetLength(range.length());
+ else
+ base_fetcher_->UnsetLength();
if (delegate_)
- delegate_->SeekToOffset(ranges_[current_index_].first);
+ delegate_->SeekToOffset(range.offset());
base_fetcher_active_ = true;
base_fetcher_->BeginTransfer(url_);
}
@@ -68,19 +75,18 @@
CHECK_LT(current_index_, ranges_.size());
CHECK_EQ(fetcher, base_fetcher_.get());
CHECK(!pending_transfer_ended_);
- off_t next_size = length;
- if (ranges_[current_index_].second >= 0) {
+ size_t next_size = length;
+ Range range = ranges_[current_index_];
+ if (range.HasLength()) {
next_size = std::min(next_size,
- ranges_[current_index_].second -
- bytes_received_this_range_);
+ range.length() - bytes_received_this_range_);
}
LOG_IF(WARNING, next_size <= 0) << "Asked to write length <= 0";
if (delegate_) {
delegate_->ReceivedBytes(this, bytes, next_size);
}
bytes_received_this_range_ += length;
- if (ranges_[current_index_].second >= 0 &&
- bytes_received_this_range_ >= ranges_[current_index_].second) {
+ if (range.HasLength() && bytes_received_this_range_ >= range.length()) {
// Terminates the current fetcher. Waits for its TransferTerminated
// callback before starting the next range so that we don't end up
// signalling the delegate that the whole multi-transfer is complete
@@ -109,8 +115,9 @@
}
// If we didn't get enough bytes, it's failure
- if (ranges_[current_index_].second >= 0) {
- if (bytes_received_this_range_ < ranges_[current_index_].second) {
+ Range range = ranges_[current_index_];
+ if (range.HasLength()) {
+ if (bytes_received_this_range_ < range.length()) {
// Failure
LOG(INFO) << "Didn't get enough bytes. Ending w/ failure.";
Reset();
@@ -155,4 +162,13 @@
bytes_received_this_range_ = 0;
}
+std::string MultiRangeHttpFetcher::Range::ToString() const {
+ std::string range_str = StringPrintf("%jd+", offset());
+ if (HasLength())
+ base::StringAppendF(&range_str, "%zu", length());
+ else
+ base::StringAppendF(&range_str, "?");
+ return range_str;
+}
+
} // namespace chromeos_update_engine
diff --git a/multi_range_http_fetcher.h b/multi_range_http_fetcher.h
index 342588a..19982ef 100644
--- a/multi_range_http_fetcher.h
+++ b/multi_range_http_fetcher.h
@@ -45,12 +45,20 @@
void ClearRanges() { ranges_.clear(); }
- void AddRange(off_t offset, off_t size) {
- ranges_.push_back(std::make_pair(offset, size));
+ void AddRange(off_t offset, size_t size) {
+ CHECK_GT(size, 0);
+ ranges_.push_back(Range(offset, size));
+ }
+
+ void AddRange(off_t offset) {
+ ranges_.push_back(Range(offset));
}
virtual void SetOffset(off_t offset) {} // for now, doesn't support this
+ virtual void SetLength(size_t length) {} // unsupported
+ virtual void UnsetLength() {}
+
// Begins the transfer to the specified URL.
// State change: Stopped -> Downloading
// (corner case: Stopped -> Stopped for an empty request)
@@ -85,8 +93,27 @@
}
private:
- // pair<offset, length>:
- typedef std::vector<std::pair<off_t, off_t> > RangesVect;
+ // 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) {}
+ 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();
@@ -120,7 +147,7 @@
RangesVect ranges_;
RangesVect::size_type current_index_; // index into ranges_
- off_t bytes_received_this_range_;
+ size_t bytes_received_this_range_;
DISALLOW_COPY_AND_ASSIGN(MultiRangeHttpFetcher);
};
diff --git a/test_http_server.cc b/test_http_server.cc
index a6410e4..af10968 100644
--- a/test_http_server.cc
+++ b/test_http_server.cc
@@ -33,6 +33,9 @@
#include "update_engine/http_fetcher_unittest.h"
+// HTTP end-of-line delimiter; sorry, this needs to be a macro.
+#define EOL "\r\n"
+
using std::min;
using std::string;
using std::vector;
@@ -40,14 +43,13 @@
namespace chromeos_update_engine {
-// HTTP end-of-line delimiter; sorry, this needs to be a macro.
-#define EOL "\r\n"
-
struct HttpRequest {
- HttpRequest() : start_offset(0), return_code(kHttpResponseOk) {}
+ HttpRequest()
+ : start_offset(0), end_offset(0), return_code(kHttpResponseOk) {}
string host;
string url;
off_t start_offset;
+ off_t end_offset; // non-inclusive, zero indicates unspecified.
HttpResponseCode return_code;
};
@@ -91,10 +93,20 @@
string &range = terms[1];
LOG(INFO) << "range attribute: " << range;
CHECK(StartsWithASCII(range, "bytes=", true) &&
- EndsWith(range, "-", true));
+ range.find('-') != string::npos);
request->start_offset = atoll(range.c_str() + strlen("bytes="));
+ // Decode end offset and increment it by one (so it is non-inclusive).
+ if (range.find('-') < range.length() - 1)
+ request->end_offset = atoll(range.c_str() + range.find('-') + 1) + 1;
request->return_code = kHttpResponsePartialContent;
- LOG(INFO) << "decoded start offset: " << request->start_offset;
+ std::string tmp_str = StringPrintf("decoded range offsets: start=%jd "
+ "end=", request->start_offset);
+ if (request->end_offset > 0)
+ base::StringAppendF(&tmp_str, "%jd (non-inclusive)",
+ request->end_offset);
+ else
+ base::StringAppendF(&tmp_str, "unspecified");
+ LOG(INFO) << tmp_str;
} else if (terms[0] == "Host:") {
CHECK_EQ(terms.size(), 2);
request->host = terms[1];
@@ -148,18 +160,21 @@
return -1;
written += ret;
- off_t content_length = end_offset;
- if (start_offset) {
+ // Compute content legnth.
+ const off_t content_length = end_offset - start_offset;;
+
+ // A start offset that equals the end offset indicates that the response
+ // should contain the full range of bytes in the requested resource.
+ if (start_offset || start_offset == end_offset) {
ret = WriteString(fd,
string("Accept-Ranges: bytes" EOL
"Content-Range: bytes ") +
- Itoa(start_offset) + "-" + Itoa(end_offset - 1) + "/" +
- Itoa(end_offset) + EOL);
+ Itoa(start_offset == end_offset ? 0 : start_offset) +
+ "-" + Itoa(end_offset - 1) + "/" + Itoa(end_offset) +
+ EOL);
if (ret < 0)
return -1;
written += ret;
-
- content_length -= start_offset;
}
ret = WriteString(fd, string("Content-Length: ") + Itoa(content_length) +
@@ -179,7 +194,7 @@
CHECK_LE(start_offset, end_offset);
CHECK_GT(line_len, 0);
- LOG(INFO) << "writing payload: " << line_len << " byte lines starting with `"
+ LOG(INFO) << "writing payload: " << line_len << "-byte lines starting with `"
<< first_byte << "', offset range " << start_offset << " -> "
<< end_offset;
@@ -236,73 +251,98 @@
}
-// Generates an HTTP response with payload corresponding to given offset and
-// length. Returns the total number of bytes delivered or -1 for error.
-ssize_t HandleGet(int fd, const HttpRequest& request, const off_t end_offset) {
- LOG(INFO) << "starting payload";
- ssize_t written = 0, ret;
-
- if ((ret = WriteHeaders(fd, request.start_offset, end_offset,
- request.return_code)) < 0)
- return -1;
- written += ret;
-
- if ((ret = WritePayload(fd, request.start_offset, end_offset)) < 0)
- return -1;
- written += ret;
-
- LOG(INFO) << "payload writing completed, " << written << " bytes written";
- return written;
-}
-
-
-// Generates an HTTP response with payload. Payload is truncated at a given
-// length. Transfer may include an optional delay midway.
-ssize_t HandleFlakyGet(int fd, const HttpRequest& request,
- const off_t max_end_offset, const off_t truncate_length,
- const int sleep_every, const int sleep_secs) {
- CHECK_GT(truncate_length, 0);
- CHECK_GT(sleep_every, 0);
- CHECK_GE(sleep_secs, 0);
-
+// Generates an HTTP response with payload corresponding to requested offsets
+// and length. Optionally, truncate the payload at a given length and add a
+// pause midway through the transfer. Returns the total number of bytes
+// delivered or -1 for error.
+ssize_t HandleGet(int fd, const HttpRequest& request, const size_t total_length,
+ const size_t truncate_length, const int sleep_every,
+ const int sleep_secs) {
ssize_t ret;
size_t written = 0;
- const off_t start_offset = request.start_offset;
- if ((ret = WriteHeaders(fd, start_offset, max_end_offset,
+ // Obtain start offset, make sure it is within total payload length.
+ const size_t start_offset = request.start_offset;
+ if (start_offset >= total_length) {
+ LOG(WARNING) << "start offset (" << start_offset
+ << ") exceeds total length (" << total_length
+ << "), generating error response ("
+ << kHttpResponseReqRangeNotSat << ")";
+ return WriteHeaders(fd, total_length, total_length,
+ kHttpResponseReqRangeNotSat);
+ }
+
+ // Obtain end offset, adjust to fit in total payload length and ensure it does
+ // not preceded the start offset.
+ size_t end_offset = (request.end_offset > 0 ?
+ request.end_offset : total_length);
+ if (end_offset < start_offset) {
+ LOG(WARNING) << "end offset (" << end_offset << ") precedes start offset ("
+ << start_offset << "), generating error response";
+ return WriteHeaders(fd, 0, 0, kHttpResponseBadRequest);
+ }
+ if (end_offset > total_length) {
+ LOG(INFO) << "requested end offset (" << end_offset
+ << ") exceeds total length (" << total_length << "), adjusting";
+ end_offset = total_length;
+ }
+
+ // Generate headers
+ LOG(INFO) << "generating response header: range=" << start_offset << "-"
+ << (end_offset - 1) << "/" << (end_offset - start_offset)
+ << ", return code=" << request.return_code;
+ if ((ret = WriteHeaders(fd, start_offset, end_offset,
request.return_code)) < 0)
return -1;
+ LOG(INFO) << ret << " header bytes written";
+ written += ret;
- const size_t content_length =
- min(truncate_length, max_end_offset - start_offset);
- const off_t end_offset = start_offset + content_length;
+ // Compute payload length, truncate as necessary.
+ size_t payload_length = end_offset - start_offset;
+ if (truncate_length > 0 && truncate_length < payload_length) {
+ LOG(INFO) << "truncating request payload length (" << payload_length
+ << ") at " << truncate_length;
+ payload_length = truncate_length;
+ end_offset = start_offset + payload_length;
+ }
- if (start_offset % (truncate_length * sleep_every) == 0) {
- const size_t midway_content_length = content_length / 2;
- const off_t midway_offset = start_offset + midway_content_length;
+ LOG(INFO) << "generating response payload: range=" << start_offset << "-"
+ << (end_offset - 1) << "/" << (end_offset - start_offset);
- LOG(INFO) << "writing small data blob of size " << midway_content_length;
+ // Decide about optional midway delay.
+ if (truncate_length > 0 && sleep_every > 0 && sleep_secs >= 0 &&
+ start_offset % (truncate_length * sleep_every) == 0) {
+ const off_t midway_offset = start_offset + payload_length / 2;
+
if ((ret = WritePayload(fd, start_offset, midway_offset)) < 0)
return -1;
+ LOG(INFO) << ret << " payload bytes written (first chunk)";
written += ret;
+ LOG(INFO) << "sleeping for " << sleep_secs << " seconds...";
sleep(sleep_secs);
- LOG(INFO) << "writing small data blob of size "
- << (content_length - midway_content_length);
if ((ret = WritePayload(fd, midway_offset, end_offset)) < 0)
return -1;
+ LOG(INFO) << ret << " payload bytes written (second chunk)";
written += ret;
} else {
- LOG(INFO) << "writing data blob of size " << content_length;
if ((ret = WritePayload(fd, start_offset, end_offset)) < 0)
return -1;
+ LOG(INFO) << ret << " payload bytes written";
written += ret;
}
+ LOG(INFO) << "response generation complete, " << written
+ << " total bytes written";
return written;
}
+ssize_t HandleGet(int fd, const HttpRequest& request,
+ const size_t total_length) {
+ return HandleGet(fd, request, total_length, 0, 0, 0);
+}
+
// Handles /redirect/<code>/<url> requests by returning the specified
// redirect <code> with a location pointing to /<url>.
void HandleRedirect(int fd, const HttpRequest& request) {
@@ -438,8 +478,8 @@
HandleGet(fd, request, terms.GetLong(1));
} else if (StartsWithASCII(url, "/flaky/", true)) {
const UrlTerms terms(url, 5);
- HandleFlakyGet(fd, request, terms.GetLong(1), terms.GetLong(2),
- terms.GetLong(3), terms.GetLong(4));
+ HandleGet(fd, request, terms.GetLong(1), terms.GetLong(2), terms.GetLong(3),
+ terms.GetLong(4));
} else if (url.find("/redirect/") == 0) {
HandleRedirect(fd, request);
} else if (url == "/error") {
diff --git a/update_attempter.cc b/update_attempter.cc
index 7ec14d0..e79f692 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -678,10 +678,10 @@
prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset);
uint64_t resume_offset = manifest_metadata_size + next_data_offset;
if (resume_offset < response_handler_action_->install_plan().size) {
- fetcher->AddRange(resume_offset, -1);
+ fetcher->AddRange(resume_offset);
}
} else {
- fetcher->AddRange(0, -1);
+ fetcher->AddRange(0);
}
}