• 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