• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
CreateCore(PartialData * owner)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 
Core(PartialData * owner)62 PartialData::Core::Core(PartialData* owner)
63     : owner_(owner), start_(0) {
64   DCHECK(!owner_->core_);
65   owner_->core_ = this;
66 }
67 
~Core()68 PartialData::Core::~Core() {
69   if (owner_)
70     owner_->core_ = NULL;
71 }
72 
Cancel()73 void PartialData::Core::Cancel() {
74   DCHECK(owner_);
75   owner_ = NULL;
76 }
77 
GetAvailableRange(disk_cache::Entry * entry,int64 offset,int len,int64 * start)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 
OnIOComplete(int result)91 void PartialData::Core::OnIOComplete(int result) {
92   if (owner_)
93     owner_->GetAvailableRangeCompleted(result, start_);
94   delete this;
95 }
96 
97 // -----------------------------------------------------------------------------
98 
PartialData()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 
~PartialData()108 PartialData::~PartialData() {
109   if (core_)
110     core_->Cancel();
111 }
112 
Init(const HttpRequestHeaders & headers)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 
SetHeaders(const HttpRequestHeaders & headers)135 void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
136   DCHECK(extra_headers_.IsEmpty());
137   extra_headers_.CopyFrom(headers);
138 }
139 
RestoreHeaders(HttpRequestHeaders * headers) const140 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 
ShouldValidateCache(disk_cache::Entry * entry,const CompletionCallback & callback)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 
PrepareCacheValidation(disk_cache::Entry * entry,HttpRequestHeaders * headers)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 
IsCurrentRangeCached() const235 bool PartialData::IsCurrentRangeCached() const {
236   return range_present_;
237 }
238 
IsLastRange() const239 bool PartialData::IsLastRange() const {
240   return final_range_;
241 }
242 
UpdateFromStoredHeaders(const HttpResponseHeaders * headers,disk_cache::Entry * entry,bool truncated)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 
SetRangeToStartDownload()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 
IsRequestedRangeOK()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 
ResponseHeadersOK(const HttpResponseHeaders * headers)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.
FixResponseHeaders(HttpResponseHeaders * headers,bool success)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 
FixContentLength(HttpResponseHeaders * headers)421 void PartialData::FixContentLength(HttpResponseHeaders* headers) {
422   headers->RemoveHeader(kLengthHeader);
423   headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
424                                         resource_size_));
425 }
426 
CacheRead(disk_cache::Entry * entry,IOBuffer * data,int data_len,const net::CompletionCallback & callback)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 
CacheWrite(disk_cache::Entry * entry,IOBuffer * data,int data_len,const net::CompletionCallback & callback)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 
OnCacheReadCompleted(int result)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 
OnNetworkReadCompleted(int result)473 void PartialData::OnNetworkReadCompleted(int result) {
474   if (result > 0)
475     current_range_start_ += result;
476 }
477 
GetNextRangeLen()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 
GetAvailableRangeCompleted(int result,int64 start)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