• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
AddRangeHeader(int64 start,int64 end,HttpRequestHeaders * headers)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.
CreateCore(PartialData * owner)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  
Core(PartialData * owner)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  
~Core()80  PartialData::Core::~Core() {
81    if (owner_)
82      owner_->core_ = NULL;
83  }
84  
Cancel()85  void PartialData::Core::Cancel() {
86    DCHECK(owner_);
87    owner_ = NULL;
88  }
89  
GetAvailableRange(disk_cache::Entry * entry,int64 offset,int len,int64 * start)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  
OnIOComplete(int result)101  void PartialData::Core::OnIOComplete(int result) {
102    if (owner_)
103      owner_->GetAvailableRangeCompleted(result, start_);
104    delete this;
105  }
106  
107  // -----------------------------------------------------------------------------
108  
PartialData()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  
~PartialData()119  PartialData::~PartialData() {
120    if (core_)
121      core_->Cancel();
122  }
123  
Init(const HttpRequestHeaders & headers)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  
SetHeaders(const HttpRequestHeaders & headers)146  void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
147    DCHECK(extra_headers_.IsEmpty());
148    extra_headers_.CopyFrom(headers);
149  }
150  
RestoreHeaders(HttpRequestHeaders * headers) const151  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  
ShouldValidateCache(disk_cache::Entry * entry,CompletionCallback * callback)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  
PrepareCacheValidation(disk_cache::Entry * entry,HttpRequestHeaders * headers)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  
IsCurrentRangeCached() const231  bool PartialData::IsCurrentRangeCached() const {
232    return range_present_;
233  }
234  
IsLastRange() const235  bool PartialData::IsLastRange() const {
236    return final_range_;
237  }
238  
UpdateFromStoredHeaders(const HttpResponseHeaders * headers,disk_cache::Entry * entry,bool truncated)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  
SetRangeToStartDownload()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  
IsRequestedRangeOK()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  
ResponseHeadersOK(const HttpResponseHeaders * headers)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.
FixResponseHeaders(HttpResponseHeaders * headers,bool success)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  
FixContentLength(HttpResponseHeaders * headers)410  void PartialData::FixContentLength(HttpResponseHeaders* headers) {
411    headers->RemoveHeader(kLengthHeader);
412    headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
413                                          resource_size_));
414  }
415  
CacheRead(disk_cache::Entry * entry,IOBuffer * data,int data_len,CompletionCallback * callback)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  
CacheWrite(disk_cache::Entry * entry,IOBuffer * data,int data_len,CompletionCallback * callback)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  
OnCacheReadCompleted(int result)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  
OnNetworkReadCompleted(int result)460  void PartialData::OnNetworkReadCompleted(int result) {
461    if (result > 0)
462      current_range_start_ += result;
463  }
464  
GetNextRangeLen()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  
GetAvailableRangeCompleted(int result,int64 start)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