AU: MultiHttpFetcher, an HttpFetcher for specific byte ranges

MultiHttpFetcher takes an HttpFetcher class via template parameter,
and a collection of byte ranges. It hits up the URL multiple times,
once per range specified. For each time, it uses a new HttpFetcher of
the type specified and fast-forwards to the offset requested, and
aborting after enough bytes have been downloaded. Any range many
specify a length of -1, which means until the end of the file (as
dictated by the server). Thus, a single range of [0, -1] makes
MultiHttpFetcher a pass-through.

HttpFetcher change: ability to supply an offset.

LibcurlHttpFetcher changes: offset support (from HttpFetcher API),
ability to be terminted in a write-callback.

test_http_fetcher: support for failures in write() on the socket (at
least in the /big url case).

BUG=7391
TEST=unittests

Review URL: http://codereview.chromium.org/3591018
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index 1dcea9e..c846812 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -81,7 +81,6 @@
 // Begins the transfer, which must not have already been started.
 void LibcurlHttpFetcher::BeginTransfer(const std::string& url) {
   transfer_size_ = -1;
-  bytes_downloaded_ = 0;
   resume_offset_ = 0;
   retry_count_ = 0;
   http_response_code_ = 0;
@@ -90,7 +89,10 @@
 }
 
 void LibcurlHttpFetcher::TerminateTransfer() {
-  CleanUp();
+  if (in_write_callback_)
+    terminate_requested_ = true;
+  else
+    CleanUp();
 }
 
 void LibcurlHttpFetcher::CurlPerformOnce() {
@@ -102,17 +104,18 @@
   // returns, so we do. libcurl promises that curl_multi_perform will not block.
   while (CURLM_CALL_MULTI_PERFORM == retcode) {
     retcode = curl_multi_perform(curl_multi_handle_, &running_handles);
+    if (terminate_requested_) {
+      CleanUp();
+      return;
+    }
   }
   if (0 == running_handles) {
-    long http_response_code = 0;
-    if (curl_easy_getinfo(curl_handle_,
-                          CURLINFO_RESPONSE_CODE,
-                          &http_response_code) == CURLE_OK) {
-      LOG(INFO) << "HTTP response code: " << http_response_code;
+    GetHttpResponseCode();
+    if (http_response_code_) {
+      LOG(INFO) << "HTTP response code: " << http_response_code_;
     } else {
       LOG(ERROR) << "Unable to get http response code.";
     }
-    http_response_code_ = static_cast<int>(http_response_code);
 
     // we're done!
     CleanUp();
@@ -135,8 +138,8 @@
     } else {
       if (delegate_) {
         // success is when http_response_code is 2xx
-        bool success = (http_response_code >= 200) &&
-            (http_response_code < 300);
+        bool success = (http_response_code_ >= 200) &&
+            (http_response_code_ < 300);
         delegate_->TransferComplete(this, success);
       }
     }
@@ -147,6 +150,7 @@
 }
 
 size_t LibcurlHttpFetcher::LibcurlWrite(void *ptr, size_t size, size_t nmemb) {
+  GetHttpResponseCode();
   {
     double transfer_size_double;
     CHECK_EQ(curl_easy_getinfo(curl_handle_,
@@ -158,8 +162,10 @@
     }
   }
   bytes_downloaded_ += size * nmemb;
+  in_write_callback_ = true;
   if (delegate_)
     delegate_->ReceivedBytes(this, reinterpret_cast<char*>(ptr), size * nmemb);
+  in_write_callback_ = false;
   return size * nmemb;
 }
 
@@ -294,4 +300,13 @@
   transfer_in_progress_ = false;
 }
 
+void LibcurlHttpFetcher::GetHttpResponseCode() {
+  long http_response_code = 0;
+  if (curl_easy_getinfo(curl_handle_,
+                        CURLINFO_RESPONSE_CODE,
+                        &http_response_code) == CURLE_OK) {
+    http_response_code_ = static_cast<int>(http_response_code);
+  }
+}
+
 }  // namespace chromeos_update_engine