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