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);
   }
 }