Home | History | Annotate | Download | only in common
      1 //
      2 // Copyright (C) 2010 The Android Open Source Project
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 
     17 #include "update_engine/common/multi_range_http_fetcher.h"
     18 
     19 #include <base/strings/stringprintf.h>
     20 
     21 #include <algorithm>
     22 #include <string>
     23 
     24 #include "update_engine/common/utils.h"
     25 
     26 namespace chromeos_update_engine {
     27 
     28 // Begins the transfer to the specified URL.
     29 // State change: Stopped -> Downloading
     30 // (corner case: Stopped -> Stopped for an empty request)
     31 void MultiRangeHttpFetcher::BeginTransfer(const std::string& url) {
     32   CHECK(!base_fetcher_active_) << "BeginTransfer but already active.";
     33   CHECK(!pending_transfer_ended_) << "BeginTransfer but pending.";
     34   CHECK(!terminating_) << "BeginTransfer but terminating.";
     35 
     36   if (ranges_.empty()) {
     37     // Note that after the callback returns this object may be destroyed.
     38     if (delegate_)
     39       delegate_->TransferComplete(this, true);
     40     return;
     41   }
     42   url_ = url;
     43   current_index_ = 0;
     44   bytes_received_this_range_ = 0;
     45   LOG(INFO) << "starting first transfer";
     46   base_fetcher_->set_delegate(this);
     47   StartTransfer();
     48 }
     49 
     50 // State change: Downloading -> Pending transfer ended
     51 void MultiRangeHttpFetcher::TerminateTransfer() {
     52   if (!base_fetcher_active_) {
     53     LOG(INFO) << "Called TerminateTransfer but not active.";
     54     // Note that after the callback returns this object may be destroyed.
     55     if (delegate_)
     56       delegate_->TransferTerminated(this);
     57     return;
     58   }
     59   terminating_ = true;
     60 
     61   if (!pending_transfer_ended_) {
     62     base_fetcher_->TerminateTransfer();
     63   }
     64 }
     65 
     66 // State change: Stopped or Downloading -> Downloading
     67 void MultiRangeHttpFetcher::StartTransfer() {
     68   if (current_index_ >= ranges_.size()) {
     69     return;
     70   }
     71 
     72   Range range = ranges_[current_index_];
     73   LOG(INFO) << "starting transfer of range " << range.ToString();
     74 
     75   bytes_received_this_range_ = 0;
     76   base_fetcher_->SetOffset(range.offset());
     77   if (range.HasLength())
     78     base_fetcher_->SetLength(range.length());
     79   else
     80     base_fetcher_->UnsetLength();
     81   if (delegate_)
     82     delegate_->SeekToOffset(range.offset());
     83   base_fetcher_active_ = true;
     84   base_fetcher_->BeginTransfer(url_);
     85 }
     86 
     87 // State change: Downloading -> Downloading or Pending transfer ended
     88 void MultiRangeHttpFetcher::ReceivedBytes(HttpFetcher* fetcher,
     89                                           const void* bytes,
     90                                           size_t length) {
     91   CHECK_LT(current_index_, ranges_.size());
     92   CHECK_EQ(fetcher, base_fetcher_.get());
     93   CHECK(!pending_transfer_ended_);
     94   size_t next_size = length;
     95   Range range = ranges_[current_index_];
     96   if (range.HasLength()) {
     97     next_size = std::min(next_size,
     98                          range.length() - bytes_received_this_range_);
     99   }
    100   LOG_IF(WARNING, next_size <= 0) << "Asked to write length <= 0";
    101   if (delegate_) {
    102     delegate_->ReceivedBytes(this, bytes, next_size);
    103   }
    104   bytes_received_this_range_ += length;
    105   if (range.HasLength() && bytes_received_this_range_ >= range.length()) {
    106     // Terminates the current fetcher. Waits for its TransferTerminated
    107     // callback before starting the next range so that we don't end up
    108     // signalling the delegate that the whole multi-transfer is complete
    109     // before all fetchers are really done and cleaned up.
    110     pending_transfer_ended_ = true;
    111     LOG(INFO) << "terminating transfer";
    112     fetcher->TerminateTransfer();
    113   }
    114 }
    115 
    116 // State change: Downloading or Pending transfer ended -> Stopped
    117 void MultiRangeHttpFetcher::TransferEnded(HttpFetcher* fetcher,
    118                                           bool successful) {
    119   CHECK(base_fetcher_active_) << "Transfer ended unexpectedly.";
    120   CHECK_EQ(fetcher, base_fetcher_.get());
    121   pending_transfer_ended_ = false;
    122   http_response_code_ = fetcher->http_response_code();
    123   LOG(INFO) << "TransferEnded w/ code " << http_response_code_;
    124   if (terminating_) {
    125     LOG(INFO) << "Terminating.";
    126     Reset();
    127     // Note that after the callback returns this object may be destroyed.
    128     if (delegate_)
    129       delegate_->TransferTerminated(this);
    130     return;
    131   }
    132 
    133   // If we didn't get enough bytes, it's failure
    134   Range range = ranges_[current_index_];
    135   if (range.HasLength()) {
    136     if (bytes_received_this_range_ < range.length()) {
    137       // Failure
    138       LOG(INFO) << "Didn't get enough bytes. Ending w/ failure.";
    139       Reset();
    140       // Note that after the callback returns this object may be destroyed.
    141       if (delegate_)
    142         delegate_->TransferComplete(this, false);
    143       return;
    144     }
    145     // We got enough bytes and there were bytes specified, so this is success.
    146     successful = true;
    147   }
    148 
    149   // If we have another transfer, do that.
    150   if (current_index_ + 1 < ranges_.size()) {
    151     current_index_++;
    152     LOG(INFO) << "Starting next transfer (" << current_index_ << ").";
    153     StartTransfer();
    154     return;
    155   }
    156 
    157   LOG(INFO) << "Done w/ all transfers";
    158   Reset();
    159   // Note that after the callback returns this object may be destroyed.
    160   if (delegate_)
    161     delegate_->TransferComplete(this, successful);
    162 }
    163 
    164 void MultiRangeHttpFetcher::TransferComplete(HttpFetcher* fetcher,
    165                                              bool successful) {
    166   LOG(INFO) << "Received transfer complete.";
    167   TransferEnded(fetcher, successful);
    168 }
    169 
    170 void MultiRangeHttpFetcher::TransferTerminated(HttpFetcher* fetcher) {
    171   LOG(INFO) << "Received transfer terminated.";
    172   TransferEnded(fetcher, false);
    173 }
    174 
    175 void MultiRangeHttpFetcher::Reset() {
    176   base_fetcher_active_ = pending_transfer_ended_ = terminating_ = false;
    177   current_index_ = 0;
    178   bytes_received_this_range_ = 0;
    179 }
    180 
    181 std::string MultiRangeHttpFetcher::Range::ToString() const {
    182   std::string range_str = base::StringPrintf("%jd+", offset());
    183   if (HasLength())
    184     range_str += std::to_string(length());
    185   else
    186     range_str += "?";
    187   return range_str;
    188 }
    189 
    190 }  // namespace chromeos_update_engine
    191