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
19namespace net {
20
21namespace {
22
23// The headers that we have to process.
24const char kLengthHeader[] = "Content-Length";
25const char kRangeHeader[] = "Content-Range";
26const 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.
32class 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
62PartialData::Core::Core(PartialData* owner)
63    : owner_(owner), start_(0) {
64  DCHECK(!owner_->core_);
65  owner_->core_ = this;
66}
67
68PartialData::Core::~Core() {
69  if (owner_)
70    owner_->core_ = NULL;
71}
72
73void PartialData::Core::Cancel() {
74  DCHECK(owner_);
75  owner_ = NULL;
76}
77
78int 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
91void PartialData::Core::OnIOComplete(int result) {
92  if (owner_)
93    owner_->GetAvailableRangeCompleted(result, start_);
94  delete this;
95}
96
97// -----------------------------------------------------------------------------
98
99PartialData::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
108PartialData::~PartialData() {
109  if (core_)
110    core_->Cancel();
111}
112
113bool 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
135void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
136  DCHECK(extra_headers_.IsEmpty());
137  extra_headers_.CopyFrom(headers);
138}
139
140void 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
159int 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
198void 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
235bool PartialData::IsCurrentRangeCached() const {
236  return range_present_;
237}
238
239bool PartialData::IsLastRange() const {
240  return final_range_;
241}
242
243bool 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  int64 length_value = headers->GetContentLength();
284  if (length_value <= 0)
285    return false;  // We must have stored the resource length.
286
287  resource_size_ = length_value;
288
289  // Make sure that this is really a sparse entry.
290  return entry->CouldBeSparse();
291}
292
293void PartialData::SetRangeToStartDownload() {
294  DCHECK(truncated_);
295  DCHECK(!sparse_entry_);
296  current_range_start_ = 0;
297  cached_start_ = 0;
298  initial_validation_ = false;
299}
300
301bool PartialData::IsRequestedRangeOK() {
302  if (byte_range_.IsValid()) {
303    if (!byte_range_.ComputeBounds(resource_size_))
304      return false;
305    if (truncated_)
306      return true;
307
308    if (current_range_start_ < 0)
309      current_range_start_ = byte_range_.first_byte_position();
310  } else {
311    // This is not a range request but we have partial data stored.
312    current_range_start_ = 0;
313    byte_range_.set_last_byte_position(resource_size_ - 1);
314  }
315
316  bool rv = current_range_start_ >= 0;
317  if (!rv)
318    current_range_start_ = 0;
319
320  return rv;
321}
322
323bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
324  if (headers->response_code() == 304) {
325    if (!byte_range_.IsValid() || truncated_)
326      return true;
327
328    // We must have a complete range here.
329    return byte_range_.HasFirstBytePosition() &&
330        byte_range_.HasLastBytePosition();
331  }
332
333  int64 start, end, total_length;
334  if (!headers->GetContentRange(&start, &end, &total_length))
335    return false;
336  if (total_length <= 0)
337    return false;
338
339  DCHECK_EQ(headers->response_code(), 206);
340
341  // A server should return a valid content length with a 206 (per the standard)
342  // but relax the requirement because some servers don't do that.
343  int64 content_length = headers->GetContentLength();
344  if (content_length > 0 && content_length != end - start + 1)
345    return false;
346
347  if (!resource_size_) {
348    // First response. Update our values with the ones provided by the server.
349    resource_size_ = total_length;
350    if (!byte_range_.HasFirstBytePosition()) {
351      byte_range_.set_first_byte_position(start);
352      current_range_start_ = start;
353    }
354    if (!byte_range_.HasLastBytePosition())
355      byte_range_.set_last_byte_position(end);
356  } else if (resource_size_ != total_length) {
357    return false;
358  }
359
360  if (truncated_) {
361    if (!byte_range_.HasLastBytePosition())
362      byte_range_.set_last_byte_position(end);
363  }
364
365  if (start != current_range_start_)
366    return false;
367
368  if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
369    return false;
370
371  return true;
372}
373
374// We are making multiple requests to complete the range requested by the user.
375// Just assume that everything is fine and say that we are returning what was
376// requested.
377void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
378                                     bool success) {
379  if (truncated_)
380    return;
381
382  if (byte_range_.IsValid() && success) {
383    headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_);
384    return;
385  }
386
387  headers->RemoveHeader(kLengthHeader);
388  headers->RemoveHeader(kRangeHeader);
389
390  if (byte_range_.IsValid()) {
391    headers->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable");
392    headers->AddHeader(base::StringPrintf("%s: bytes 0-0/%" PRId64,
393                                          kRangeHeader, resource_size_));
394    headers->AddHeader(base::StringPrintf("%s: 0", kLengthHeader));
395  } else {
396    // TODO(rvargas): Is it safe to change the protocol version?
397    headers->ReplaceStatusLine("HTTP/1.1 200 OK");
398    DCHECK_NE(resource_size_, 0);
399    headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
400                                          resource_size_));
401  }
402}
403
404void PartialData::FixContentLength(HttpResponseHeaders* headers) {
405  headers->RemoveHeader(kLengthHeader);
406  headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
407                                        resource_size_));
408}
409
410int PartialData::CacheRead(
411    disk_cache::Entry* entry, IOBuffer* data, int data_len,
412    const net::CompletionCallback& callback) {
413  int read_len = std::min(data_len, cached_min_len_);
414  if (!read_len)
415    return 0;
416
417  int rv = 0;
418  if (sparse_entry_) {
419    rv = entry->ReadSparseData(current_range_start_, data, read_len,
420                               callback);
421  } else {
422    if (current_range_start_ > kint32max)
423      return ERR_INVALID_ARGUMENT;
424
425    rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
426                         data, read_len, callback);
427  }
428  return rv;
429}
430
431int PartialData::CacheWrite(
432    disk_cache::Entry* entry, IOBuffer* data, int data_len,
433    const net::CompletionCallback& callback) {
434  DVLOG(3) << "To write: " << data_len;
435  if (sparse_entry_) {
436    return entry->WriteSparseData(
437        current_range_start_, data, data_len, callback);
438  } else  {
439    if (current_range_start_ > kint32max)
440      return ERR_INVALID_ARGUMENT;
441
442    return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
443                            data, data_len, callback, true);
444  }
445}
446
447void PartialData::OnCacheReadCompleted(int result) {
448  DVLOG(3) << "Read: " << result;
449  if (result > 0) {
450    current_range_start_ += result;
451    cached_min_len_ -= result;
452    DCHECK_GE(cached_min_len_, 0);
453  }
454}
455
456void PartialData::OnNetworkReadCompleted(int result) {
457  if (result > 0)
458    current_range_start_ += result;
459}
460
461int PartialData::GetNextRangeLen() {
462  int64 range_len =
463      byte_range_.HasLastBytePosition() ?
464      byte_range_.last_byte_position() - current_range_start_ + 1 :
465      kint32max;
466  if (range_len > kint32max)
467    range_len = kint32max;
468  return static_cast<int32>(range_len);
469}
470
471void PartialData::GetAvailableRangeCompleted(int result, int64 start) {
472  DCHECK(!callback_.is_null());
473  DCHECK_NE(ERR_IO_PENDING, result);
474
475  cached_start_ = start;
476  cached_min_len_ = result;
477  if (result >= 0)
478    result = 1;  // Return success, go ahead and validate the entry.
479
480  CompletionCallback cb = callback_;
481  callback_.Reset();
482  cb.Run(result);
483}
484
485}  // namespace net
486