Home | History | Annotate | Download | only in http
      1 // Copyright (c) 2009 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_util.h"
     10 #include "net/base/net_errors.h"
     11 #include "net/disk_cache/disk_cache.h"
     12 #include "net/http/http_response_headers.h"
     13 #include "net/http/http_util.h"
     14 
     15 namespace {
     16 
     17 // The headers that we have to process.
     18 const char kLengthHeader[] = "Content-Length";
     19 const char kRangeHeader[] = "Content-Range";
     20 const int kDataStream = 1;
     21 
     22 }
     23 
     24 namespace net {
     25 
     26 bool PartialData::Init(const std::string& headers) {
     27   std::vector<HttpByteRange> ranges;
     28   if (!HttpUtil::ParseRanges(headers, &ranges) || ranges.size() != 1)
     29     return false;
     30 
     31   // We can handle this range request.
     32   byte_range_ = ranges[0];
     33   if (!byte_range_.IsValid())
     34     return false;
     35 
     36   resource_size_ = 0;
     37   current_range_start_ = byte_range_.first_byte_position();
     38   return true;
     39 }
     40 
     41 void PartialData::SetHeaders(const std::string& headers) {
     42   DCHECK(extra_headers_.empty());
     43   extra_headers_ = headers;
     44 }
     45 
     46 void PartialData::RestoreHeaders(std::string* headers) const {
     47   DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
     48   int64 end = byte_range_.IsSuffixByteRange() ?
     49               byte_range_.suffix_length() : byte_range_.last_byte_position();
     50 
     51   headers->assign(extra_headers_);
     52   if (byte_range_.IsValid())
     53     AddRangeHeader(current_range_start_, end, headers);
     54 }
     55 
     56 int PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
     57                                         std::string* headers) {
     58   DCHECK(current_range_start_ >= 0);
     59 
     60   // Scan the disk cache for the first cached portion within this range.
     61   int64 range_len = byte_range_.HasLastBytePosition() ?
     62       byte_range_.last_byte_position() - current_range_start_ + 1 : kint32max;
     63   if (range_len > kint32max)
     64     range_len = kint32max;
     65   int len = static_cast<int32>(range_len);
     66   if (!len)
     67     return 0;
     68   range_present_ = false;
     69 
     70   if (sparse_entry_) {
     71     cached_min_len_ = entry->GetAvailableRange(current_range_start_, len,
     72                                                &cached_start_);
     73   } else if (truncated_) {
     74     if (!current_range_start_) {
     75       // Update the cached range only the first time.
     76       cached_min_len_ = static_cast<int32>(byte_range_.first_byte_position());
     77       cached_start_ = 0;
     78     }
     79   } else {
     80     cached_min_len_ = len;
     81     cached_start_ = current_range_start_;
     82   }
     83 
     84   if (cached_min_len_ < 0) {
     85     DCHECK(cached_min_len_ != ERR_IO_PENDING);
     86     return cached_min_len_;
     87   }
     88 
     89   headers->assign(extra_headers_);
     90 
     91   if (!cached_min_len_) {
     92     // We don't have anything else stored.
     93     final_range_ = true;
     94     cached_start_ =
     95         byte_range_.HasLastBytePosition() ? current_range_start_  + len : 0;
     96   }
     97 
     98   if (current_range_start_ == cached_start_) {
     99     // The data lives in the cache.
    100     range_present_ = true;
    101     if (len == cached_min_len_)
    102       final_range_ = true;
    103     AddRangeHeader(current_range_start_, cached_start_ + cached_min_len_ - 1,
    104                    headers);
    105   } else {
    106     // This range is not in the cache.
    107     AddRangeHeader(current_range_start_, cached_start_ - 1, headers);
    108   }
    109 
    110   // Return a positive number to indicate success (versus error or finished).
    111   return 1;
    112 }
    113 
    114 bool PartialData::IsCurrentRangeCached() const {
    115   return range_present_;
    116 }
    117 
    118 bool PartialData::IsLastRange() const {
    119   return final_range_;
    120 }
    121 
    122 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
    123                                           disk_cache::Entry* entry,
    124                                           bool truncated) {
    125   resource_size_ = 0;
    126   if (truncated) {
    127     DCHECK_EQ(headers->response_code(), 200);
    128     // We don't have the real length and the user may be trying to create a
    129     // sparse entry so let's not write to this entry.
    130     if (byte_range_.IsValid())
    131       return false;
    132 
    133     // Now we avoid resume if there is no content length, but that was not
    134     // always the case so double check here.
    135     int64 total_length = headers->GetContentLength();
    136     if (total_length <= 0 || !headers->HasStrongValidators())
    137       return false;
    138 
    139     truncated_ = true;
    140     sparse_entry_ = false;
    141     byte_range_.set_first_byte_position(entry->GetDataSize(kDataStream));
    142     resource_size_ = total_length;
    143     current_range_start_ = 0;
    144     return true;
    145   }
    146 
    147   if (headers->response_code() == 200) {
    148     DCHECK(byte_range_.IsValid());
    149     sparse_entry_ = false;
    150     resource_size_ = entry->GetDataSize(kDataStream);
    151     return true;
    152   }
    153 
    154   int64 length_value = headers->GetContentLength();
    155   if (length_value <= 0)
    156     return false;  // We must have stored the resource length.
    157 
    158   resource_size_ = length_value;
    159 
    160   // Make sure that this is really a sparse entry.
    161   int64 n;
    162   if (ERR_CACHE_OPERATION_NOT_SUPPORTED == entry->GetAvailableRange(0, 5, &n))
    163     return false;
    164 
    165   return true;
    166 }
    167 
    168 bool PartialData::IsRequestedRangeOK() {
    169   if (byte_range_.IsValid()) {
    170     if (truncated_)
    171       return true;
    172     if (!byte_range_.ComputeBounds(resource_size_))
    173       return false;
    174 
    175     if (current_range_start_ < 0)
    176       current_range_start_ = byte_range_.first_byte_position();
    177   } else {
    178     // This is not a range request but we have partial data stored.
    179     current_range_start_ = 0;
    180     byte_range_.set_last_byte_position(resource_size_ - 1);
    181   }
    182 
    183   bool rv = current_range_start_ >= 0;
    184   if (!rv)
    185     current_range_start_ = 0;
    186 
    187   return rv;
    188 }
    189 
    190 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
    191   if (headers->response_code() == 304) {
    192     if (!byte_range_.IsValid() || truncated_)
    193       return true;
    194 
    195     // We must have a complete range here.
    196     return byte_range_.HasFirstBytePosition() &&
    197            byte_range_.HasLastBytePosition();
    198   }
    199 
    200   int64 start, end, total_length;
    201   if (!headers->GetContentRange(&start, &end, &total_length))
    202     return false;
    203   if (total_length <= 0)
    204     return false;
    205 
    206   int64 content_length = headers->GetContentLength();
    207   if (content_length < 0 || content_length != end - start + 1)
    208     return false;
    209 
    210   if (!resource_size_) {
    211     // First response. Update our values with the ones provided by the server.
    212     resource_size_ = total_length;
    213     if (!byte_range_.HasFirstBytePosition()) {
    214       byte_range_.set_first_byte_position(start);
    215       current_range_start_ = start;
    216     }
    217     if (!byte_range_.HasLastBytePosition())
    218       byte_range_.set_last_byte_position(end);
    219   } else if (resource_size_ != total_length) {
    220     return false;
    221   }
    222 
    223   if (truncated_) {
    224     if (!byte_range_.HasLastBytePosition())
    225       byte_range_.set_last_byte_position(end);
    226   }
    227 
    228   if (start != current_range_start_)
    229     return false;
    230 
    231   if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
    232     return false;
    233 
    234   return true;
    235 }
    236 
    237 // We are making multiple requests to complete the range requested by the user.
    238 // Just assume that everything is fine and say that we are returning what was
    239 // requested.
    240 void PartialData::FixResponseHeaders(HttpResponseHeaders* headers) {
    241   if (truncated_)
    242     return;
    243 
    244   headers->RemoveHeader(kLengthHeader);
    245   headers->RemoveHeader(kRangeHeader);
    246 
    247   int64 range_len;
    248   if (byte_range_.IsValid()) {
    249     if (!sparse_entry_)
    250       headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
    251 
    252     DCHECK(byte_range_.HasFirstBytePosition());
    253     DCHECK(byte_range_.HasLastBytePosition());
    254     headers->AddHeader(
    255         StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
    256                      kRangeHeader,
    257                      byte_range_.first_byte_position(),
    258                      byte_range_.last_byte_position(),
    259                      resource_size_));
    260     range_len = byte_range_.last_byte_position() -
    261                 byte_range_.first_byte_position() + 1;
    262   } else {
    263     // TODO(rvargas): Is it safe to change the protocol version?
    264     headers->ReplaceStatusLine("HTTP/1.1 200 OK");
    265     DCHECK_NE(resource_size_, 0);
    266     range_len = resource_size_;
    267   }
    268 
    269   headers->AddHeader(StringPrintf("%s: %" PRId64, kLengthHeader, range_len));
    270 }
    271 
    272 void PartialData::FixContentLength(HttpResponseHeaders* headers) {
    273   headers->RemoveHeader(kLengthHeader);
    274   headers->AddHeader(StringPrintf("%s: %" PRId64, kLengthHeader,
    275                                   resource_size_));
    276 }
    277 
    278 int PartialData::CacheRead(disk_cache::Entry* entry, IOBuffer* data,
    279                            int data_len, CompletionCallback* callback) {
    280   int read_len = std::min(data_len, cached_min_len_);
    281   if (!read_len)
    282     return 0;
    283 
    284   int rv = 0;
    285   if (sparse_entry_) {
    286     rv = entry->ReadSparseData(current_range_start_, data, read_len,
    287                                callback);
    288   } else {
    289     if (current_range_start_ > kint32max)
    290       return ERR_INVALID_ARGUMENT;
    291 
    292     rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
    293                          data, read_len, callback);
    294   }
    295   return rv;
    296 }
    297 
    298 int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
    299                            int data_len, CompletionCallback* callback) {
    300   if (sparse_entry_) {
    301     return entry->WriteSparseData(current_range_start_, data, data_len,
    302                                   callback);
    303   } else  {
    304     if (current_range_start_ > kint32max)
    305       return ERR_INVALID_ARGUMENT;
    306 
    307     return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
    308                             data, data_len, callback, true);
    309   }
    310 }
    311 
    312 void PartialData::OnCacheReadCompleted(int result) {
    313   if (result > 0) {
    314     current_range_start_ += result;
    315     cached_min_len_ -= result;
    316     DCHECK(cached_min_len_ >= 0);
    317   }
    318 }
    319 
    320 void PartialData::OnNetworkReadCompleted(int result) {
    321   if (result > 0)
    322     current_range_start_ += result;
    323 }
    324 
    325 // Static.
    326 void PartialData::AddRangeHeader(int64 start, int64 end, std::string* headers) {
    327   DCHECK(start >= 0 || end >= 0);
    328   std::string my_start, my_end;
    329   if (start >= 0)
    330     my_start = Int64ToString(start);
    331   if (end >= 0)
    332     my_end = Int64ToString(end);
    333 
    334   headers->append(StringPrintf("Range: bytes=%s-%s\r\n", my_start.c_str(),
    335                                my_end.c_str()));
    336 }
    337 
    338 }  // namespace net
    339