Home | History | Annotate | Download | only in http
      1 // Copyright (c) 2011 The Chromium 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 
      5 #include "net/http/partial_data.h"
      6 
      7 #include "base/format_macros.h"
      8 #include "base/logging.h"
      9 #include "base/string_number_conversions.h"
     10 #include "base/string_util.h"
     11 #include "base/stringprintf.h"
     12 #include "net/base/net_errors.h"
     13 #include "net/disk_cache/disk_cache.h"
     14 #include "net/http/http_response_headers.h"
     15 #include "net/http/http_util.h"
     16 
     17 namespace net {
     18 
     19 namespace {
     20 
     21 // The headers that we have to process.
     22 const char kLengthHeader[] = "Content-Length";
     23 const char kRangeHeader[] = "Content-Range";
     24 const int kDataStream = 1;
     25 
     26 void AddRangeHeader(int64 start, int64 end, HttpRequestHeaders* headers) {
     27   DCHECK(start >= 0 || end >= 0);
     28   std::string my_start, my_end;
     29   if (start >= 0)
     30     my_start = base::Int64ToString(start);
     31   if (end >= 0)
     32     my_end = base::Int64ToString(end);
     33 
     34   headers->SetHeader(
     35       HttpRequestHeaders::kRange,
     36       base::StringPrintf("bytes=%s-%s", my_start.c_str(), my_end.c_str()));
     37 }
     38 
     39 }  // namespace
     40 
     41 // A core object that can be detached from the Partialdata object at destruction
     42 // so that asynchronous operations cleanup can be performed.
     43 class PartialData::Core {
     44  public:
     45   // Build a new core object. Lifetime management is automatic.
     46   static Core* CreateCore(PartialData* owner) {
     47     return new Core(owner);
     48   }
     49 
     50   // Wrapper for Entry::GetAvailableRange. If this method returns ERR_IO_PENDING
     51   // PartialData::GetAvailableRangeCompleted() will be invoked on the owner
     52   // object when finished (unless Cancel() is called first).
     53   int GetAvailableRange(disk_cache::Entry* entry, int64 offset, int len,
     54                         int64* start);
     55 
     56   // Cancels a pending operation. It is a mistake to call this method if there
     57   // is no operation in progress; in fact, there will be no object to do so.
     58   void Cancel();
     59 
     60  private:
     61   explicit Core(PartialData* owner);
     62   ~Core();
     63 
     64   // Pending io completion routine.
     65   void OnIOComplete(int result);
     66 
     67   PartialData* owner_;
     68   int64 start_;
     69   net::CompletionCallbackImpl<Core> callback_;
     70   DISALLOW_COPY_AND_ASSIGN(Core);
     71 };
     72 
     73 PartialData::Core::Core(PartialData* owner)
     74     : owner_(owner),
     75       ALLOW_THIS_IN_INITIALIZER_LIST(callback_(this, &Core::OnIOComplete)) {
     76   DCHECK(!owner_->core_);
     77   owner_->core_ = this;
     78 }
     79 
     80 PartialData::Core::~Core() {
     81   if (owner_)
     82     owner_->core_ = NULL;
     83 }
     84 
     85 void PartialData::Core::Cancel() {
     86   DCHECK(owner_);
     87   owner_ = NULL;
     88 }
     89 
     90 int PartialData::Core::GetAvailableRange(disk_cache::Entry* entry, int64 offset,
     91                                          int len, int64* start) {
     92   int rv = entry->GetAvailableRange(offset, len, &start_, &callback_);
     93   if (rv != net::ERR_IO_PENDING) {
     94     // The callback will not be invoked. Lets cleanup.
     95     *start = start_;
     96     delete this;
     97   }
     98   return rv;
     99 }
    100 
    101 void PartialData::Core::OnIOComplete(int result) {
    102   if (owner_)
    103     owner_->GetAvailableRangeCompleted(result, start_);
    104   delete this;
    105 }
    106 
    107 // -----------------------------------------------------------------------------
    108 
    109 PartialData::PartialData()
    110     : range_present_(false),
    111       final_range_(false),
    112       sparse_entry_(true),
    113       truncated_(false),
    114       initial_validation_(false),
    115       core_(NULL),
    116       callback_(NULL) {
    117 }
    118 
    119 PartialData::~PartialData() {
    120   if (core_)
    121     core_->Cancel();
    122 }
    123 
    124 bool PartialData::Init(const HttpRequestHeaders& headers) {
    125   std::string range_header;
    126   if (!headers.GetHeader(HttpRequestHeaders::kRange, &range_header))
    127     return false;
    128 
    129   std::vector<HttpByteRange> ranges;
    130   if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1)
    131     return false;
    132 
    133   // We can handle this range request.
    134   byte_range_ = ranges[0];
    135   if (!byte_range_.IsValid())
    136     return false;
    137 
    138   resource_size_ = 0;
    139   current_range_start_ = byte_range_.first_byte_position();
    140 
    141   DVLOG(1) << "Range start: " << current_range_start_ << " end: " <<
    142                byte_range_.last_byte_position();
    143   return true;
    144 }
    145 
    146 void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
    147   DCHECK(extra_headers_.IsEmpty());
    148   extra_headers_.CopyFrom(headers);
    149 }
    150 
    151 void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const {
    152   DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
    153   int64 end = byte_range_.IsSuffixByteRange() ?
    154               byte_range_.suffix_length() : byte_range_.last_byte_position();
    155 
    156   headers->CopyFrom(extra_headers_);
    157   if (!truncated_ && byte_range_.IsValid())
    158     AddRangeHeader(current_range_start_, end, headers);
    159 }
    160 
    161 int PartialData::ShouldValidateCache(disk_cache::Entry* entry,
    162                                      CompletionCallback* callback) {
    163   DCHECK_GE(current_range_start_, 0);
    164 
    165   // Scan the disk cache for the first cached portion within this range.
    166   int len = GetNextRangeLen();
    167   if (!len)
    168     return 0;
    169 
    170   DVLOG(3) << "ShouldValidateCache len: " << len;
    171 
    172   if (sparse_entry_) {
    173     DCHECK(!callback_);
    174     Core* core = Core::CreateCore(this);
    175     cached_min_len_ = core->GetAvailableRange(entry, current_range_start_, len,
    176                                               &cached_start_);
    177 
    178     if (cached_min_len_ == ERR_IO_PENDING) {
    179       callback_ = callback;
    180       return ERR_IO_PENDING;
    181     }
    182   } else if (!truncated_) {
    183     if (byte_range_.HasFirstBytePosition() &&
    184         byte_range_.first_byte_position() >= resource_size_) {
    185       // The caller should take care of this condition because we should have
    186       // failed IsRequestedRangeOK(), but it's better to be consistent here.
    187       len = 0;
    188     }
    189     cached_min_len_ = len;
    190     cached_start_ = current_range_start_;
    191   }
    192 
    193   if (cached_min_len_ < 0)
    194     return cached_min_len_;
    195 
    196   // Return a positive number to indicate success (versus error or finished).
    197   return 1;
    198 }
    199 
    200 void PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
    201                                          HttpRequestHeaders* headers) {
    202   DCHECK_GE(current_range_start_, 0);
    203   DCHECK_GE(cached_min_len_, 0);
    204 
    205   int len = GetNextRangeLen();
    206   DCHECK_NE(0, len);
    207   range_present_ = false;
    208 
    209   headers->CopyFrom(extra_headers_);
    210 
    211   if (!cached_min_len_) {
    212     // We don't have anything else stored.
    213     final_range_ = true;
    214     cached_start_ =
    215         byte_range_.HasLastBytePosition() ? current_range_start_  + len : 0;
    216   }
    217 
    218   if (current_range_start_ == cached_start_) {
    219     // The data lives in the cache.
    220     range_present_ = true;
    221     if (len == cached_min_len_)
    222       final_range_ = true;
    223     AddRangeHeader(current_range_start_, cached_start_ + cached_min_len_ - 1,
    224                    headers);
    225   } else {
    226     // This range is not in the cache.
    227     AddRangeHeader(current_range_start_, cached_start_ - 1, headers);
    228   }
    229 }
    230 
    231 bool PartialData::IsCurrentRangeCached() const {
    232   return range_present_;
    233 }
    234 
    235 bool PartialData::IsLastRange() const {
    236   return final_range_;
    237 }
    238 
    239 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
    240                                           disk_cache::Entry* entry,
    241                                           bool truncated) {
    242   resource_size_ = 0;
    243   if (truncated) {
    244     DCHECK_EQ(headers->response_code(), 200);
    245     // We don't have the real length and the user may be trying to create a
    246     // sparse entry so let's not write to this entry.
    247     if (byte_range_.IsValid())
    248       return false;
    249 
    250     // Now we avoid resume if there is no content length, but that was not
    251     // always the case so double check here.
    252     int64 total_length = headers->GetContentLength();
    253     if (total_length <= 0 || !headers->HasStrongValidators())
    254       return false;
    255 
    256     truncated_ = true;
    257     initial_validation_ = true;
    258     sparse_entry_ = false;
    259     int current_len = entry->GetDataSize(kDataStream);
    260     byte_range_.set_first_byte_position(current_len);
    261     resource_size_ = total_length;
    262     current_range_start_ = current_len;
    263     cached_min_len_ = current_len;
    264     cached_start_ = current_len + 1;
    265     return true;
    266   }
    267 
    268   if (headers->response_code() == 200) {
    269     DCHECK(byte_range_.IsValid());
    270     sparse_entry_ = false;
    271     resource_size_ = entry->GetDataSize(kDataStream);
    272     DVLOG(2) << "UpdateFromStoredHeaders size: " << resource_size_;
    273     return true;
    274   }
    275 
    276   if (!headers->HasStrongValidators())
    277     return false;
    278 
    279   int64 length_value = headers->GetContentLength();
    280   if (length_value <= 0)
    281     return false;  // We must have stored the resource length.
    282 
    283   resource_size_ = length_value;
    284 
    285   // Make sure that this is really a sparse entry.
    286   return entry->CouldBeSparse();
    287 }
    288 
    289 void PartialData::SetRangeToStartDownload() {
    290   DCHECK(truncated_);
    291   DCHECK(!sparse_entry_);
    292   current_range_start_ = 0;
    293   cached_start_ = 0;
    294   initial_validation_ = false;
    295 }
    296 
    297 bool PartialData::IsRequestedRangeOK() {
    298   if (byte_range_.IsValid()) {
    299     if (!byte_range_.ComputeBounds(resource_size_))
    300       return false;
    301     if (truncated_)
    302       return true;
    303 
    304     if (current_range_start_ < 0)
    305       current_range_start_ = byte_range_.first_byte_position();
    306   } else {
    307     // This is not a range request but we have partial data stored.
    308     current_range_start_ = 0;
    309     byte_range_.set_last_byte_position(resource_size_ - 1);
    310   }
    311 
    312   bool rv = current_range_start_ >= 0;
    313   if (!rv)
    314     current_range_start_ = 0;
    315 
    316   return rv;
    317 }
    318 
    319 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
    320   if (headers->response_code() == 304) {
    321     if (!byte_range_.IsValid() || truncated_)
    322       return true;
    323 
    324     // We must have a complete range here.
    325     return byte_range_.HasFirstBytePosition() &&
    326         byte_range_.HasLastBytePosition();
    327   }
    328 
    329   int64 start, end, total_length;
    330   if (!headers->GetContentRange(&start, &end, &total_length))
    331     return false;
    332   if (total_length <= 0)
    333     return false;
    334 
    335   int64 content_length = headers->GetContentLength();
    336   if (content_length < 0 || content_length != end - start + 1)
    337     return false;
    338 
    339   if (!resource_size_) {
    340     // First response. Update our values with the ones provided by the server.
    341     resource_size_ = total_length;
    342     if (!byte_range_.HasFirstBytePosition()) {
    343       byte_range_.set_first_byte_position(start);
    344       current_range_start_ = start;
    345     }
    346     if (!byte_range_.HasLastBytePosition())
    347       byte_range_.set_last_byte_position(end);
    348   } else if (resource_size_ != total_length) {
    349     return false;
    350   }
    351 
    352   if (truncated_) {
    353     if (!byte_range_.HasLastBytePosition())
    354       byte_range_.set_last_byte_position(end);
    355   }
    356 
    357   if (start != current_range_start_)
    358     return false;
    359 
    360   if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
    361     return false;
    362 
    363   return true;
    364 }
    365 
    366 // We are making multiple requests to complete the range requested by the user.
    367 // Just assume that everything is fine and say that we are returning what was
    368 // requested.
    369 void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
    370                                      bool success) {
    371   if (truncated_)
    372     return;
    373 
    374   headers->RemoveHeader(kLengthHeader);
    375   headers->RemoveHeader(kRangeHeader);
    376 
    377   int64 range_len, start, end;
    378   if (byte_range_.IsValid()) {
    379     if (success) {
    380       if (!sparse_entry_)
    381         headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
    382 
    383       DCHECK(byte_range_.HasFirstBytePosition());
    384       DCHECK(byte_range_.HasLastBytePosition());
    385       start = byte_range_.first_byte_position();
    386       end = byte_range_.last_byte_position();
    387       range_len = end - start + 1;
    388     } else {
    389       headers->ReplaceStatusLine(
    390           "HTTP/1.1 416 Requested Range Not Satisfiable");
    391       start = 0;
    392       end = 0;
    393       range_len = 0;
    394     }
    395 
    396     headers->AddHeader(
    397         base::StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
    398                            kRangeHeader, start, end, resource_size_));
    399   } else {
    400     // TODO(rvargas): Is it safe to change the protocol version?
    401     headers->ReplaceStatusLine("HTTP/1.1 200 OK");
    402     DCHECK_NE(resource_size_, 0);
    403     range_len = resource_size_;
    404   }
    405 
    406   headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
    407                                         range_len));
    408 }
    409 
    410 void PartialData::FixContentLength(HttpResponseHeaders* headers) {
    411   headers->RemoveHeader(kLengthHeader);
    412   headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
    413                                         resource_size_));
    414 }
    415 
    416 int PartialData::CacheRead(disk_cache::Entry* entry, IOBuffer* data,
    417                            int data_len, CompletionCallback* callback) {
    418   int read_len = std::min(data_len, cached_min_len_);
    419   if (!read_len)
    420     return 0;
    421 
    422   int rv = 0;
    423   if (sparse_entry_) {
    424     rv = entry->ReadSparseData(current_range_start_, data, read_len,
    425                                callback);
    426   } else {
    427     if (current_range_start_ > kint32max)
    428       return ERR_INVALID_ARGUMENT;
    429 
    430     rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
    431                          data, read_len, callback);
    432   }
    433   return rv;
    434 }
    435 
    436 int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
    437                             int data_len, CompletionCallback* callback) {
    438   DVLOG(3) << "To write: " << data_len;
    439   if (sparse_entry_) {
    440     return entry->WriteSparseData(current_range_start_, data, data_len,
    441                                   callback);
    442   } else  {
    443     if (current_range_start_ > kint32max)
    444       return ERR_INVALID_ARGUMENT;
    445 
    446     return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
    447                             data, data_len, callback, true);
    448   }
    449 }
    450 
    451 void PartialData::OnCacheReadCompleted(int result) {
    452   DVLOG(3) << "Read: " << result;
    453   if (result > 0) {
    454     current_range_start_ += result;
    455     cached_min_len_ -= result;
    456     DCHECK_GE(cached_min_len_, 0);
    457   }
    458 }
    459 
    460 void PartialData::OnNetworkReadCompleted(int result) {
    461   if (result > 0)
    462     current_range_start_ += result;
    463 }
    464 
    465 int PartialData::GetNextRangeLen() {
    466   int64 range_len =
    467       byte_range_.HasLastBytePosition() ?
    468       byte_range_.last_byte_position() - current_range_start_ + 1 :
    469       kint32max;
    470   if (range_len > kint32max)
    471     range_len = kint32max;
    472   return static_cast<int32>(range_len);
    473 }
    474 
    475 void PartialData::GetAvailableRangeCompleted(int result, int64 start) {
    476   DCHECK(callback_);
    477   DCHECK_NE(ERR_IO_PENDING, result);
    478 
    479   cached_start_ = start;
    480   cached_min_len_ = result;
    481   if (result >= 0)
    482     result = 1;  // Return success, go ahead and validate the entry.
    483 
    484   CompletionCallback* cb = callback_;
    485   callback_ = NULL;
    486   cb->Run(result);
    487 }
    488 
    489 }  // namespace net
    490